洛谷P2136 拉近距离(负环判定)
题目背景
我是源点,你是终点。我们之间有负权环。 ——小明
题目描述
在小明和小红的生活中,有 NNN 个关键的节点。有 MMM 个事件,记为一个三元组 (Si,Ti,Wi)(S_i,T_i,W_i)(Si,Ti,Wi),表示从节点 SiS_iSi 有一个事件可以转移到 TiT_iTi,事件的效果就是使他们之间的距离减少 WiW_iWi。
这些节点构成了一个网络,其中节点 111 和 NNN 是特殊的,节点 111 代表小明,节点 NNN 代表小红,其他代表进展的阶段。所有事件可以自由选择是否进行,但每次只能进行当前节点邻接的。请你帮他们写一个程序,计算出他们之间可能的最短距离。
输入格式
第一行,两个正整数 N,MN,MN,M。
之后 MMM 行,每行 333 个空格隔开的整数 Si,Ti,WiS_i,T_i,W_iSi,Ti,Wi。
输出格式
一行,一个整数表示他们之间可能的最短距离。如果这个距离可以无限缩小,输出Forever love
。
输入输出样例
输入 #1
3 3 1 2 3 2 3 -1 3 1 -10
输出 #1
-2
由于是减少,所以要存负边,转化为负环判定+最短路问题。注意:有可能是小明找小红也可能是小红找小明!
所以整个流程是:小明为起点SPFA->有负环直接输出,无负环更新答案(取最小)->小红为起点SPFA->有负环直接输出,无负环更新答案->输出答案
#include <bits/stdc++.h> #define N 1005 #define M 10005 using namespace std; int n,m,tot=0; int head[N],edge[2*M],ver[2*M],Next[2*M],cnt[N],d[N]; bool v[N]; void add(int x,int y,int z) { ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot; } bool spfa(int num) { queue<int>q; d[num]=0,v[num]=1,cnt[num]=0; q.push(num); while(q.size()) { int x=q.front(); q.pop(); v[x]=0; int i; for(i=head[x];i;i=Next[i]) { int y=ver[i],z=edge[i]; if(d[y]>d[x]+z) { d[y]=d[x]+z; cnt[y]++; if(cnt[y]>=n)return 1; if(!v[y])q.push(y),v[y]=1; } } } return 0; } int main() { memset(head,0,sizeof(head)); memset(edge,0,sizeof(edge)); memset(ver,0,sizeof(ver)); memset(Next,0,sizeof(Next)); memset(cnt,0,sizeof(cnt)); memset(v,0,sizeof(v)); memset(d,0x3f,sizeof(d)); tot=0; cin>>n>>m; int i,x,y,w; for(i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&w); add(x,y,-w); } int ans=0x3f3f3f3f; bool flag1=spfa(1); if(flag1) { cout<<"Forever love"; return 0; } ans=d[n]; memset(cnt,0,sizeof(cnt)); memset(v,0,sizeof(v)); memset(d,0x3f,sizeof(d)); bool falg2=spfa(n); if(flag1) { cout<<"Forever love"; return 0; } ans=min(ans,d[1]); cout<<ans; return 0; }