每日一题 wpy的请求 (spfa)
wpy的请求
https://ac.nowcoder.com/acm/problem/20684
一.题意
给出 n 个点和 m条边的有向图,边权可能为负值,修改任意边权,使所有边权非负且任意<u,v>的最短路路径不变。
二.题解
关键点在于两个:
- 建图。最短路+负边权不难联想到 ,由于是单源最短路,所以要增加一个超级源点,保证原本的 n 个点都有对应的最短路。
- 边权。最短路松弛条件为 ,松弛形式为 ,所以跑完 后肯定成立的有对于任意 : 。
参考官方题解的证明:
以超级源点存在为前提,对于最短路 ,有:
保证所有的最短路方案都不变,我们考虑使得新的最短只与旧最短路以及起点终点有关,与其他的值都无关,
所以我们只需要在在最短路松弛中消去其他的值,考虑对于任意边,令其边权为
(上面已经证得为非负数),就可以保证对每条最短路的影响只与 有关。
对于原最短路 ,有:
三.代码:
#include<bits/stdc++.h> #define mp make_pair #define pb push_back #define ll long long #define fi first #define se second #define inf 0x3f3f3f3f #define eps 1e-10 #define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; const int manx=1e5+5; const int N=5e3+5; const int mod=1e9+7; ll head[N],d[N]; bool vis[N]; struct node{ ll next,v,w,u; }a[manx]; ll n,m,s,e,k; void add(ll u,ll v,ll w){ a[++k].v=v; a[k].u=u; a[k].next=head[u]; a[k].w=w; head[u]=k; } void spfa(){ queue<ll>q; for(int i=0;i<=n;i++) d[i]=inf,vis[i]=0; q.push(s); vis[s]=1; d[s]=0; while(q.size()){ ll u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i;i=a[i].next){ ll v=a[i].v,w=a[i].w; if(d[v]>d[u]+w){ d[v]=d[u]+w; if(!vis[v]) q.push(v),vis[v]=1; } } } } int main(){ io; cin>>n>>m; ll u,v,w; for(int i=1;i<=m;i++){ cin>>u>>v>>w; add(u,v,w); } for(int i=1;i<=n;i++) add(0,i,0); spfa(); for(int i=1;i<=m;i++){ u=a[i].u,v=a[i].v,w=a[i].w; printf("%lld %lld %lld\n",u,v,d[u]-d[v]+w); } return 0; }