题目背景
我是源点,你是终点。我们之间有负权环。 ——小明
题目描述
在小明和小红的生活中,有N个关键的节点。有M个事件,记为一个三元组(Si,Ti,Wi),表示从节点Si有一个事件可以转移到Ti,事件的效果就是使他们之间的距离减少Wi。
这些节点构成了一个网络,其中节点1和N是特殊的,节点1代表小明,节点N代表小红,其他代表进展的阶段。所有事件可以自由选择是否进行,但每次只能进行当前节点邻接的。请你帮他们写一个程序,计算出他们之间可能的最短距离。
输入输出格式
输入格式:第1行,两个正整数N,M.
之后M行,每行3个空格隔开的整数Si,Ti,Wi。
一行,一个整数表示他们之间可能的最短距离。如果这个距离可以无限缩小,输出“Forever love”(不含引号)。
输入输出样例
说明
对于20%数据,N<=10,M<=50。
对于50%数据,N<=300,M<=5000。
对于全部数据,N<=1000,M<=10000,|Wi|<=100,保证从节点1到N有路径。
裸的 spfa+负环,就是有几个坑点:
1.注意在输入的时候要 乘 -1
2.“拉近距离”的不一定是小明,也可能是小红。
题目中没有给出具体的源点汇点(不算迷惑人的题目背景的话),所以在做spfa的时候,分别以1和n为源点各做一遍,取min,才能得到正确答案。n=999的点就是在小红主动的情况下的解。
所以这是一道语文模板题(雾。。。)
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#define MAXN 1010
#define MAX 999999999
using namespace std;
int n,m,ans,c=1;
int head[MAXN],path[MAXN],rank[MAXN];
bool flag=false,vis[MAXN];
struct node{
int next,to,w;
}a[MAXN*10];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline int relax(int u,int v,int w){
if(path[v]>path[u]+w){
path[v]=path[u]+w;
return 1;
}
return 0;
}
inline void add(int u,int v,int w){
a[c].to=v;a[c].w=w;
a[c].next=head[u];
head[u]=c++;
}
void spfa(int s,int t){
int u,v;
queue<int> q;
for(int i=1;i<=n;i++){path[i]=MAX;vis[i]=false;rank[i]=0;}
path[s]=0;
vis[s]=true;
rank[s]=1;
q.push(s);
while(!q.empty()){
u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];i;i=a[i].next){
v=a[i].to;
if(relax(u,v,a[i].w)&&!vis[v]){
vis[v]=true;
rank[v]++;
if(rank[v]>=n){
flag=true;
break;
}
q.push(v);
}
}
}
ans=min(ans,path[t]);
}
int main(){
int u,v,w;
n=read();m=read();
for(int i=1;i<=m;i++){
u=read();v=read();w=read();
add(u,v,-w);
}
ans=MAX;
if(!flag)spfa(1,n);
if(!flag)spfa(n,1);
if(flag)printf("Forever love\n");
else printf("%d\n",ans);
return 0;
}