分层图上的最短路。
f[i][j]表示走到i结点,费用为j的最少时间,直接跑spfa即可。
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cstdlib> #include <queue> #define inf 0x3f3f3f3f #define maxc (n-1)*100 using namespace std; struct edge { int y,ne,c,t; }e[1000]; struct now { int p,c; }; int tot=0,n,m; struct data { int t,f; }d[105][10005]; int s,t,h[105],inq[105][10005]; void Addedge(int x,int y,int co,int ti) { tot++; e[tot].y=y; e[tot].ne=h[x]; h[x]=tot; e[tot].c=co; e[tot].t=ti; } void spfa() { for (int i=1;i<=n;i++) for (int j=0;j<=maxc;j++) d[i][j].f=0,inq[i][j]=0,d[i][j].t=inf; queue<now> q; now x; x.p=s,x.c=0; d[s][0].f=1,d[s][0].t=0; inq[s][0]=1; q.push(x); while (!q.empty()) { x=q.front(); q.pop(); inq[x.p][x.c]=0; for (int i=h[x.p];i;i=e[i].ne) { int y=e[i].y; int co=e[i].c+x.c; if (co>maxc) continue; if (d[y][co].t>d[x.p][x.c].t+e[i].t) { d[y][co].t=d[x.p][x.c].t+e[i].t; d[y][co].f=1; if (!inq[y][co]) { now aa; aa.p=y,aa.c=co; q.push(aa),inq[y][co]=1; } } } } } int main() { scanf("%d%d%d%d",&n,&m,&s,&t); for (int i=1;i<=m;i++) { int x,y,ti,co; scanf("%d%d%d%d",&x,&y,&co,&ti); Addedge(x,y,co,ti); Addedge(y,x,co,ti); } spfa(); int ans=0,minn=maxc+10; for (int i=0;i<=maxc;i++) { if (!d[t][i].f) continue; if (d[t][i].t>=minn) continue; minn=d[t][i].t; ans++; } cout<<ans<<endl; return 0; }
wa是因为求答案时写了break,应该是continue。
其实这个题还可以进行优化:
如果存在f[i][k],满足k<j且f[i][k]<f[i][j],那么f[i][j]一定不是最优解。
因此我们可以给每一个结点建一棵线段树,维护最小值即可。
#include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <cstdlib> #include <queue> #define inf 0x3f3f3f3f #define maxc (n-1)*100+5 using namespace std; struct edge { int y,ne,c,t; }e[1000]; struct now { int p,c; }; int tot=0,n,m; struct data { int t,f; }d[105][10005]; int s,t,h[105],inq[105][10005]; struct segtree { int l,r,minn; }T[105][40005]; void Addedge(int x,int y,int co,int ti) { tot++; e[tot].y=y; e[tot].ne=h[x]; h[x]=tot; e[tot].c=co; e[tot].t=ti; } void Build(int now,int x,int l,int r) { T[now][x].l=l,T[now][x].r=r; if (l==r) { T[now][x].minn=maxc; return; } int m=(l+r)>>1; Build(now,x<<1,l,m); Build(now,(x<<1)+1,m+1,r); T[now][x].minn=maxc; } void Update(int now,int x,int p,int k) { if (T[now][x].l==T[now][x].r&&T[now][x].l==p) { T[now][x].minn=min(k,T[now][x].minn); return; } int m=(T[now][x].l+T[now][x].r)>>1; if (p<=m) Update(now,x<<1,p,k); else Update(now,(x<<1)+1,p,k); T[now][x].minn=min(T[now][x<<1].minn,T[now][(x<<1)+1].minn); } int Get(int now,int x,int l,int r) { if (T[now][x].l>=l&&T[now][x].r<=r) return T[now][x].minn; int m=(T[now][x].l+T[now][x].r)>>1; if (r<=m) return Get(now,x<<1,l,r); if (l>m+1) return Get(now,(x<<1)+1,l,r); return min(Get(now,x<<1,l,r),Get(now,(x<<1)+1,l,r)); } void spfa() { for (int i=1;i<=n;i++) for (int j=0;j<=maxc;j++) d[i][j].f=0,inq[i][j]=0,d[i][j].t=inf; queue<now> q; now x; x.p=s,x.c=0; d[s][0].f=1,d[s][0].t=0; inq[s][0]=1; Update(s,1,0,0); q.push(x); while (!q.empty()) { x=q.front(); q.pop(); inq[x.p][x.c]=0; for (int i=h[x.p];i;i=e[i].ne) { int y=e[i].y; int co=e[i].c+x.c; if (co>maxc) continue; if (d[y][co].t>d[x.p][x.c].t+e[i].t&&Get(y,1,0,co)>d[x.p][x.c].t+e[i].t) { d[y][co].t=d[x.p][x.c].t+e[i].t; d[y][co].f=1; Update(y,1,co,d[y][co].t); if (!inq[y][co]) { now aa; aa.p=y,aa.c=co; q.push(aa),inq[y][co]=1; } } } } } int main() { scanf("%d%d%d%d",&n,&m,&s,&t); for (int i=1;i<=m;i++) { int x,y,ti,co; scanf("%d%d%d%d",&x,&y,&co,&ti); Addedge(x,y,co,ti); Addedge(y,x,co,ti); } for (int i=1;i<=n;i++) Build(i,1,0,maxc); spfa(); int ans=0,minn=maxc+10; for (int i=0;i<=maxc;i++) { if (!d[t][i].f) continue; if (d[t][i].t>=minn) continue; minn=d[t][i].t; ans++; } cout<<ans<<endl; return 0; }
进入第一版~
其实没必要用线段树,因为是求前缀的最小值,直接树状数组就可以了。。