题目:洛谷P2176。
题目大意:有n个点m条无向边,一个人要从1走到n,他会走最短路。现在可以让一条边的长度翻倍,求翻倍后这个人要多走多少距离。
解题思路:首先可以知道,翻倍肯定是在最短路上的某条边翻,否则他走的路不会变。我们先跑一遍最短路,记录下走的边,再枚举哪条边翻倍,然后跑最短路,记录下答案即可。
此题好像卡SPFA,于是我用堆优化Dijkstra秒杀。
时间复杂度$O(nm\log n)$。
C++ Code:
#include<cstdio>
#include<cstring>
#include<ext/pb_ds/priority_queue.hpp>
using namespace __gnu_pbds;
struct edge{int from,to,dis,nxt;
}e[10005];
struct heapnode{int u,d;bool operator<(const heapnode& rhs)const{return d>rhs.d;}
};
priority_queue<heapnode>q;
int n,m,head[10005],cnt,d[10005],prev[10005],prea[10005];
inline void addedge(int from,int to,int dis){e[++cnt]=(edge){from,to,dis,head[from]};head[from]=cnt;e[++cnt]=(edge){to,from,dis,head[to]};head[to]=cnt;
}
void dijkstra(){memset(d,0x3f,sizeof d);memset(prev,0,sizeof prev);q.push((heapnode){1,d[1]=0});while(!q.empty()){heapnode x=q.top();q.pop();int u=x.u;if(d[u]!=x.d)continue;for(int i=head[u];i;i=e[i].nxt){int v=e[i].to;if(d[v]>d[u]+e[i].dis){d[v]=d[u]+e[i].dis;prev[v]=i;q.push((heapnode){v,d[v]});}}}
}
int main(){cnt=1;scanf("%d%d",&n,&m);for(;m--;){int x,y,z;scanf("%d%d%d",&x,&y,&z);addedge(x,y,z);}dijkstra();memcpy(prea,prev,sizeof prea);int ans=d[n],mx=0;for(int i=prea[n];i;i=prea[e[i].from]){e[i].dis*=2;e[i^1].dis*=2;dijkstra();if(d[n]-ans>mx)mx=d[n]-ans;e[i].dis/=2;e[i^1].dis/=2;}printf("%d\n",mx);return 0;
}