1. 程式人生 > >洛谷 P2850 [USACO06DEC]蟲洞Wormholes

洛谷 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;
}