题目翻译
在探索他的许多农场时,农夫约翰发现了许多惊人的虫洞。 虫洞非常特殊,因为它是一条单向路径,可在您进入虫洞之前将您送至目的地! FJ的每个农场均包含N(1≤N≤500)个田地,方便地编号为1…N,M(1≤M≤2500)条路径和W(1≤W≤200)虫洞。
由于FJ是一位狂热的时空旅行爱好者,因此他希望做到以下几点:从某个领域开始,经过一些路径和虫洞,并在他初次离开之前的某个时间返回开始领域。 也许他将能够见到自己:)。
为了帮助FJ查明是否可行,他将向您提供其农场F(1≤F≤5)的完整地图。 路径行驶时间不会超过10,000秒,虫洞也不会使FJ的返回时间超过10,000秒。
输入输出
第1行:单个整数F.牧场描述。
每个服务器场的第1行:三个以空格分隔的整数:N,M和W
每个服务器场的第2…M + 1行:三个以空格分隔的数字(S,E,T),分别描述:S和E之间的双向路径,需要经过T秒。 两个字段可能通过一条以上的路径连接。
每个农场的M + 2…M + W + 1行:分别描述的三个以空格分隔的数字(S,E,T):从S到E的单向路径也使旅行者退回T秒。
思路分析
起始点是没有给定的,也就是说我们从任何一点开始,只要存在能回到自身的权重之和为负的路径即可。
两种方法:spfa或者floyd,floyd显然时间复杂度更高但是这道题里也是可以用的:
Tips:
- 农场与农场之间路径是双向的。
- 两个位置链接的路径不止一条,需要去重
floyd
对于floyd算法而言,有负环也就意味着dp[i][i]至少有一个小于0.注意这里面我们不再使用min函数,而是使用《判断再赋值。
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int map[2005][2005]={0};
int main()
{
int n,m,s;
int t;
scanf("%d",&t);
getchar();
while(t--)
{
scanf("%d%d%d",&n,&m,&s);
// printf("%d %d %d\n",n,m,s);
for(int i=1;i<=n;i++)
{
map[i][i]=0;
for(int j=1;j<=n;j++)
{
map[i][j]=inf;
}
}
int u,v,w;
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&w);
if(map[u][v]>w)
map[u][v]=map[v][u]=w;
}
for(int i=0;i<s;i++)
{
scanf("%d%d%d",&u,&v,&w);
map[u][v]=-w;
}
int flag=0;
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(map[i][j]>map[i][k]+map[k][j])
map[i][j]=map[i][k]+map[k][j];
}
if(map[i][i]<0)
{
flag=1;
break;
}
}
if(flag==1)
break;
}
if(flag==1)
printf("YES\n");
else
printf("NO\n");
}
}
效果不是特别好,那么我们如果使用bellman-ford的升级版。spfa呢?
SPFA
对于spfa而言,我们可以随意指定一个初始点s,如果他经过的点数大于n个,显然就是存在负环了。
#include <iostream>
#include<vector>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
#define inf 10000000
#define MAX 2005 //农场+虫洞
struct edge {
int to, cost;
edge(int a, int b) { to = a, cost = b; }
};
vector<edge> G[MAX];
int vis[MAX], d[MAX];
int main()
{
int f;
cin >> f;
while (f--) {
int n, m, w, a, b, c;
cin >> n >> m >> w;
memset(vis, 0, sizeof(vis));
fill(d, d + MAX, 0);
for (int i = 1; i <= n; i++)G[i].clear();
for (int i = 0; i < m; i++) {
cin >> a >> b >> c;
G[a].push_back(edge(b, c));
G[b].push_back(edge(a, c));
}
for (int i = 0; i < w; i++) {
cin >> a >> b >> c;
G[a].push_back(edge(b, -c));
}
int cnt = 0, sign = 1;
for (int i = 0; i < n && sign; i++) {//不断的更新
for (int j = 1; j <= n && sign; j++) {
for (int k = 0; k < G[j].size(); k++) {
edge e = G[j][k];
if (d[e.to] > d[j] + e.cost) {
d[e.to] = d[j] + e.cost;
if (i == n - 1) { sign = 0; break; }//第n次更新最短路径都有变化
}
}
}
}
if (sign)cout << "NO" << endl;
else cout << "YES" << endl;
}
}
自己对图论还是不熟啊,准备写一篇超长总结了~~