Holy Grail————計蒜客
阿新 • • 發佈:2021-01-14
題目連結
https://nanti.jisuanke.com/t/41305/
思路
題意是給一個n個點m條有向邊的圖,題目保證可能會存在負權邊,不存在重邊和自環,也不存在負環,然後給出六條邊的起點u和終點v,題目保證在新增前不會有能從u到達v的路徑。
每次新增都要保證:第一,按照題意,是要新增一條反向邊將本來能從v到u的最短路變成權重為0(題目說花費,一個意思)。第二,新增之後不能有負環,第三,新增後的邊會影響後面的新增。所以牽扯到負環那麼本題自然採用SPFA演算法(沒用Bellman_Ford是因為第一複雜度不如SPFA,第二不習慣0.0),求出v到u的最短路後新增上負號即可。
為什麼可以直接新增負號這裡寫一下我的證明想法,可能不太規範,如果搜到最短路是正的,那麼直接在起終點新增一個負權邊不會有負環,因為是它是所有路徑的最小值且為正,那麼就算取負號也不會影響其它路徑成為負環;如果是負的同理,會找到所有路徑的最小值且為負,那麼這個負值取相反數之後就會比其它所有路徑的絕對值都要大,那麼它一定能讓所有形成的環都變成正值;如果搜不到最短路,就代表存在負環,但是題目保證了初始圖不存在負環,故這個情況不存在。
c++程式碼
#include<bits/stdc++.h>
using namespace std;
const int INF=1e9+2;
const int N=310;
int n,m;
int g[N][N];
int dis[N],vis[N];
void SPFA(int u){
memset(dis, INF, sizeof(dis));
memset(vis,0,sizeof(vis));
dis[u] = 0;
vis[u] = 1;
queue<int> q;
q.push(u);
while(q.size())
{
auto t=q.front();
q.pop();
vis[t]=0;
for (int i=0;i<n;i++)
{
if(dis[i]>dis[t]+g[t][i])
{
dis[i]=dis[t]+g[t][i];
if(!vis[i])
{
vis[i]=1;
q.push(i);
}
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
memset(g,INF,sizeof(g));
for(int i=0;i<m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
g[a][b]=c;
}
for(int i=0;i<6;i++)
{
int u,v;
scanf("%d%d",&u,&v);
SPFA(v);
g[u][v]=-dis[u];
printf("%d\n",g[u][v]);
}
}
return 0;
}