题目链接 HDU5168
【题意】一个有向图,给定起点终点,每条边上有权值。
一条合法的路径定义为相邻边的权值之差不小于K的路径,即路径上每条边的权值至少要比上一条边的权值大K ,如果上一条边存在。合法路径的长度定义为路径上的边权值总和。
求从起点到终点的合法路径的最短长度。
【分析】用dijkstra竟然也能过,多加一个la[]维护每个点的上一条边的花费,需要注意的是当c当前边cost<la[v]时也要更新(即需要让每个la[]劲量保持最小以得到后面更多的可能),这样变形的dijkstra中dis[]不一定和队列中的dis相等,还有一点:不是一个点访问过就不能访问,而是当新的cost<la[v]时也要入队(就是上面说过的更新)。具体看代码吧。
而标程应该是用N个单调队列对每个点维护上一条边的权值和到当前点的最短距离,具体看代码。
【AC CODE(dijkstra)】967ms
数据有点弱,vis标记放在入队的时候都对了,导致跑了592ms侥幸排第一了,其实是错的,下面的才是对的,需要把更新放在开始(即出队下方)
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <map>
//#include <unordered_map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int MAXN = 100000+10, MAXM = 200000+10;
const LL INF = 4557430888798830399;
struct Edge{
int next,to,cost;
Edge(int a = 0, int b = 0, int c = 0){
next = a, to = b, cost = c;
}
}edge[MAXM];
struct NODE{
LL dis, la;//这里的dis不一定是dis[u]
int u;
NODE(LL a = 0, LL b = 0, int c = 0){ dis = a, la = b, u = c;}
bool operator <(const NODE& a) const {return dis > a.dis;}
};
int tol,head[MAXN], k;
LL dis[MAXN], la[MAXN];
bool vis[MAXN];
void add_edge(int u, int v, int cost)
{
edge[tol] = Edge(head[u],v,cost);
head[u] = tol++;
}
void dijkstra(int st)
{
priority_queue<NODE> q;
clc(dis, 0x3f);
clc(vis, 0);
clc(la, 0x3f);
dis[st] = 0;
la[st] = -INF;
q.push(NODE(dis[st], la[st], st));
while (!q.empty())
{
NODE now = q.top(); q.pop();
int u = now.u;
if(dis[u] > now.dis) dis[u] = now.dis;
if(vis[u] && la[u] <= now.la) continue;
vis[u] = true;
la[u] = now.la;
for (int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].to;
LL cost = edge[i].cost;
/*下面不能用dis[u]替换now.dis 两者不一定相等*/
if (cost - la[u] >= k && (dis[v] > now.dis + cost || cost < la[v]))
q.push(NODE(now.dis + cost, cost, v));
}
}
}
int main()
{
#ifdef SHY
freopen("e:\\1.txt", "r", stdin);
#endif
int t;
scanf("%d%*c", &t);
while(t--)
{
int n,m;
scanf("%d %d %d%*c", &n, &m, &k);
int a,b,c;
tol = 0;
clc(head,-1);
rep(i,0,m)
{
scanf("%d %d %d%*c", &a, &b, &c);
add_edge(a,b,c);
}
dijkstra(1);
if(INF == dis[n]) puts("-1");
else printf("%I64d\n", dis[n]);
}
return 0;
}
/*
1
6 7 2
1 2 2
2 3 4
3 4 6
1 5 3
5 4 8
4 6 8
4 6 10
=======
20
*/
【AC CODE(单调队列DP)】1425ms
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <map>
//#include <unordered_map>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int MAXN = 100000+10, MAXM = 200000+10;
const LL INF = 4557430888798830399;
struct NODE{
LL c;//c是上一条边的花费
LL val;// val是i点的最短距离
NODE(LL a, LL b){
c = a, val = b;
}
};
struct Edge{
int u,v,cost;
bool operator<(const Edge& t)const{
return cost < t.cost;
}
}edge[MAXM];
deque<NODE> q[MAXN];
LL dp[MAXN];
int main()
{
#ifdef SHY
freopen("e:\\1.txt", "r", stdin);
#endif
int t;
scanf("%d%*c", &t);
while(t--)
{
int n,m,k;
scanf("%d %d %d%*c", &n, &m, &k);
repe(i,1,n) q[i].clear();
rep(i,0,m) scanf("%d %d %d%*c", &edge[i].u, &edge[i].v, &edge[i].cost);
sort(edge,edge+m);
clc(dp,0x3f);
repe(i,1,n) q[i].push_back(NODE(-INF,INF));
dp[1] = 0;
rep(i,0,m)
{
int u = edge[i].u, v = edge[i].v, cost = edge[i].cost;
NODE nxt = NODE(cost,INF);
if(1 == u)
nxt.val = cost;
else
{
//找到上一条边合法的最小值,先删除队列中不合法的
while(q[u].size() > 1 && cost-k >= q[u][1].c)
q[u].pop_front();
nxt.val = q[u][0].val+cost;
}
if(nxt.val < q[v][q[v].size()-1].val)
{
while(q[v].size() > 1 && nxt.val >= q[v][q[v].size()-1].val) q[v].pop_back();
q[v].push_back(nxt);
}
dp[v] = min(dp[v], nxt.val);
}
if(INF == dp[n]) puts("-1");
else printf("%I64d\n", dp[n]);
}
return 0;
}