洛谷 P2850 [USACO06DEC]蟲洞Wormholes
題目描述
While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way path that delivers you to its destination at a time that is BEFORE you entered the wormhole! Each of FJ's farms comprises N (1 ≤ N ≤ 500) fields conveniently numbered 1..N, M (1 ≤ M ≤ 2500) paths, and W (1 ≤ W ≤ 200) wormholes.
As FJ is an avid time-traveling fan, he wants to do the following: start at some field, travel through some paths and wormholes, and return to the starting field a time before his initial departure. Perhaps he will be able to meet himself :) .
To help FJ find out whether this is possible or not, he will supply you with complete maps to F (1 ≤ F ≤ 5) of his farms. No paths will take longer than 10,000 seconds to travel and no wormhole can bring FJ back in time by more than 10,000 seconds.
John在他的農場中閒逛時發現了許多蟲洞。蟲洞可以看作一條十分奇特的有向邊,並可以使你返回到過去的一個時刻(相對你進入蟲洞之前)。John的每個農場有M條小路(無向邊)連線著N (從1..N標號)塊地,並有W個蟲洞。其中1<=N<=500,1<=M<=2500,1<=W<=200。 現在John想借助這些蟲洞來回到過去(出發時刻之前),請你告訴他能辦到嗎。 John將向你提供F(1<=F<=5)個農場的地圖。沒有小路會耗費你超過10000秒的時間,當然也沒有蟲洞回幫你回到超過10000秒以前。
輸入輸出格式
輸入格式:Line 1: A single integer, F. F farm descriptions follow.
Line 1 of each farm: Three space-separated integers respectively: N, M, and W
Lines 2..M+1 of each farm: Three space-separated numbers (S, E, T) that describe, respectively: a bidirectional path between S and E that requires T seconds to traverse. Two fields might be connected by more than one path.
Lines M+2..M+W+1 of each farm: Three space-separated numbers (S, E, T) that describe, respectively: A one way path from S to E that also moves the traveler back T seconds.
輸出格式:Lines 1..F: For each farm, output "YES" if FJ can achieve his goal, otherwise output "NO" (do not include the quotes).
輸入輸出樣例
輸入樣例#1:2 3 3 1 1 2 2 1 3 4 2 3 1 3 1 3 3 2 1 1 2 3 2 3 4 3 1 8輸出樣例#1:
NO YES
說明
For farm 1, FJ cannot travel back in time.
For farm 2, FJ could travel back in time by the cycle 1->2->3->1, arriving back at his starting location 1 second before he leaves. He could start from anywhere on the cycle to accomplish this.
題目的大意應該已經很清楚了。
不過在此之前我也沒做過負環的題,然後就按著最短路的思路寫了一下。
最初我是直接跑一遍BFS版的SPFA,然後記錄下遍歷過的點。
如果某個點遍歷過2次,那麼就有環,否則無環。
然後一遍過了樣例就直接交上去了。然後不加優化果然是T了的。
然後某位dalao告訴我負環可以用DFS版的SPFA。
其實兩個版本的SPFA都差不多。
思路和註釋差不多都在程式碼裡了 其實是我好像沒什麼好講的了233。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN=10001;
int k,n,m,w,tot=0;
int Head[MAXN],Dis[MAXN];//Head表示鄰接表的頭點,Dis[i]表示從起始點到i點的當前距離。
bool visit[MAXN],flag=0;//visit記錄是否遍歷到了這個點。
struct edge //鄰接表存圖
{
int next,node,w;
}h[MAXN*4];
void add(int u,int v,int w)//加點
{
h[++tot].next=Head[u];
h[tot].node=v;
h[tot].w=w;
Head[u]=tot; //雖然有重邊但是不用去判斷了。
}
void SPFA(int x)
{
if(flag)
return ;
visit[x]=1; //記錄下所有遍歷的點
for(int i=Head[x];i;i=h[i].next) //和跑最短路基本一樣
{
if(flag)
return ;
int v=h[i].node;
if(Dis[v]>Dis[x]+h[i].w)
{
Dis[v]=Dis[x]+h[i].w;
if(visit[v]) //如果已經遍歷過了就直接跳出去了。
{
flag=1;
return ;
}
else
SPFA(v); //繼續遍歷
}
}
visit[x]=0; //回溯的時候將遍歷過的點還原
}
int main()
{
cin>>k;
while(k--)
{
tot=0;//每個資料要記得清0,
flag=0;
memset(h,0,sizeof(h));
memset(visit,0,sizeof(visit));
memset(Head,0,sizeof(Head));
memset(Dis,0,sizeof(Dis));//這裡需要注意一下,Dis初始設定成0能跳過很多路徑,
cin>>n>>m>>w; //直接找到負數點所在的環。
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z); //正權邊是雙向的需要注意一下。
}
for(int i=1;i<=w;i++)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y,-z); //這道題很方便的就是負權邊都放一起了不用去打判斷。
}
for(int i=1;i<=n;i++) //其實吧,這裡隨便找個點SPFA都行,畢竟有負環從哪個點開始都能找到,
{ //而且只要跑一遍就行了。
if(flag)
break; //發現負環直接退出迴圈。
SPFA(i);
}
if(flag)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}