Wormholes
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 30671 | Accepted: 11123 |
Description
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, andW (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 toF (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.
Input
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 fromS toE that also moves the traveler backT seconds.
Output
Lines 1..F: For each farm, output “YES” if FJ can achieve his goal, otherwise output “NO” (do not include the quotes).
Sample Input
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
Sample Output
NO
YES
Hint
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.
Source
题目链接: http://poj.org/problem?id=3259
题目大意: 有F个农场, 每个农场有N(1 <= N <= 500)块地, 编号从1~N,这N块地间有M(1 <= M <= 2500)条路,W(1 <= W <= 200)个虫洞,两块地间的通路是双向的,虫洞是单向的,给出可走路径花费的时间和虫洞回退的时间让你判断一个人从起点通过某条回路是否可能将时间倒退
题解: 本题背景略坑人,实际上就是要判断一个带负权的有向网中是否存在负权值回路, 开始学的Bellman-Ford算法, 可是其效率偏低,[算法复杂度为o(m*n)] 存在大量冗余操作, 这里考虑用SPFA算法实现. 用SPFA算法判断是否存在负权值回路的原理是: 若存在负权值回路,那么从源点到某个顶点的最短路径可以无限缩短,某些顶点入队列将超过顶点的个数N,因此只需要在SPFA算法中统计每个顶点入队列的次数,在取出队列的头顶点时,都判断该顶点入队列的次数是否已经超过N次,若是则说明存在负权值回路,则可退出,退出时队列可能不为空,处理前要清空队列. 对于图论的题,建图是个难点,此题顶点路径都给出,直接根据数据建图即可,然后使用SPFA判断出答案.
SPFA:
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int const INF = 100000000;
int const MAXN = 500 + 10;
struct Node //节点结构体,包含了该节点的一些信息
{
int to; //该节点指向的节点
int weight; //指向节点路径的权值
Node *next; //next指针
};
queue <int> q; //队列中的节点为图中顶点序号
int N, M, W; //农场个数, 双向路径, 单向虫洞
Node *List[MAXN]; //每个顶点的边链表表头指针数组
int inq[MAXN]; //每个顶点是否在队列中的标志
int count[MAXN]; //每个顶点入队列的次数
int dist[MAXN], path[MAXN]; //记录权值和路径
bool SPFA(int v0)
{
int u; // u为队列头顶点序号
Node * temp;
for(int i = 1; i <= N; i++) //初始化
{
dist[i] = INF;
path[i] = v0;
inq[i] = 0;
}
dist[v0] = 0;
path[v0] = v0;
inq[v0] = 1;
q.push(v0); //将起点压入队列
count[v0]++;
while(!q.empty())
{
u = q.front();
q.pop();
inq[u] = 0;
if(count[u] > N) //若头顶点进队次数大于图上点数则返回true
return true;
temp = List[u];
while(temp != NULL)
{
int v = temp -> to;
if(dist[v] > dist[u] + temp -> weight) //若该路可松弛则将其松弛
{
dist[v] = dist[u] + temp -> weight;
path[v] = u;
if(!inq[v])
{
q.push(v);
inq[v] = 1;
count[v]++;
}
}
temp = temp -> next;
}
}
return false;
}
int main()
{
int T;
int u, v, w; //边的起点,终点及权值
scanf("%d",&T);
for(int i = 1; i <= T; i++)
{
scanf("%d%d%d",&N, &M, &W);
memset(List,0,sizeof(List));
memset(inq,0,sizeof(inq));
memset(count,0,sizeof(count));
while(!q.empty())
q.pop(); //清空队列
Node *temp;
for(int j = 1; j <= M; j++) //读入双向边
{
scanf("%d%d%d",&u, &v, &w); //读入边的起点终点和权值
temp = new Node; //生成temp节点
temp -> to = v; //temp节点指向v节点
temp -> weight = w; //路径权值为w
temp -> next = NULL; //next指针指向空
if(List[u] == NULL) //若边链表的表头指针数组为空 则赋temp值
List[u] = temp;
else //否则交换变量
{
temp -> next = List[u];
List[u] = temp;
}
temp = new Node; //操作同上,因为是双向的
temp -> to = u;
temp -> weight = w;
temp -> next = NULL;
if(List[v] == NULL)
List[v] = temp;
else
{
temp -> next = List[v];
List[v] = temp;
}
}
for(int j = 1; j <= W; j++) //读入单向边
{
scanf("%d%d%d",&u, &v, &w);
temp = new Node;
temp -> to = v;
temp -> weight = -w;
temp -> next = NULL;
if(List[u] == NULL)
List[u] = temp;
else
{
temp -> next = List[u];
List[u] = temp;
}
}
if(SPFA(1))
printf("YES\n");
else
printf("NO\n");
for(int j = 1; j <= N; j++) //释放边链表上各边节点所占的存储空间
{
temp = List[j];
while(temp != NULL)
{
List[j] = temp -> next;
delete temp;
temp = List[j];
}
}
}
}
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
int const INF = 0x3fffffff;
int const MAX = 505;
int dis[MAX], cnt[MAX];
bool vis[MAX];
int n, m, w;
struct EDGE
{
int u, v;
int val;
}e[MAX * MAX / 2];
struct NODE
{
int v, w;
NODE(int vv, int ww)
{
v = vv;
w = ww;
}
};
vector <NODE> vt[MAX];
bool SPFA(int v0)
{
memset(cnt, 0, sizeof(cnt));
memset(vis, false, sizeof(vis));
queue <int> q;
for(int i = 1; i <= n; i++)
dis[i] = INF;
dis[v0] = 0;
q.push(v0);
cnt[v0] ++;
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
int sz = vt[u].size();
for(int j = 0; j < sz; j++)
{
int v = vt[u][j].v;
int w = vt[u][j].w;
if(dis[v] > dis[u] + w)
{
dis[v] = dis[u] + w;
if(!vis[v])
{
vis[v] = true;
q.push(v);
cnt[v] ++;
if(cnt[v] >= n)
return true;
}
}
}
}
return false;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
for(int i = 0; i <= n; i++)
vt[i].clear();
scanf("%d %d %d", &n, &m, &w);
for(int i = 0; i < m; i++)
scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].val);
for(int i = m; i < m + w; i++)
{
scanf("%d %d %d", &e[i].u, &e[i].v, &e[i].val);
e[i].val = -e[i].val;
}
for(int i = 0; i < m; i++)
{
vt[e[i].u].push_back(NODE(e[i].v, e[i].val));
vt[e[i].v].push_back(NODE(e[i].u, e[i].val));
}
for(int i = m; i < m + w; i++)
vt[e[i].u].push_back(NODE(e[i].v, e[i].val));
bool flag = false;
for(int i = 1; i <= n; i++)
{
if(SPFA(i))
{
flag = true;
break;
}
}
if(flag)
printf("YES\n");
else
printf("NO\n");
}
}
Bellman – Ford:
#include <cstdio>
#include <cstring>
int const INF = 0x3fffffff;
struct Edge
{
int u, v, val;
}e[100005];
int n, m, w, cnt;
int dis[505];
void Add(int u, int v, int val)
{
e[cnt].u = u;
e[cnt].v = v;
e[cnt++].val = val;
}
bool Bellman_ford()
{
memset(dis, 0, sizeof(dis));
for(int i = 0; i < n; i++)
{
for(int j = 0; j < cnt; j++)
{
if(dis[e[j].u] > dis[e[j].v] + e[j].val)
{
dis[e[j].u] = dis[e[j].v] + e[j].val;
if(i == n - 1)
return true;
}
}
}
return false;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
cnt = 0;
scanf("%d %d %d", &n, &m, &w);
int u, v, val;
while(m--)
{
scanf("%d %d %d", &u, &v, &val);
Add(u, v, val);
Add(v, u, val);
}
while(w--)
{
scanf("%d %d %d", &u, &v, &val);
Add(u, v, -val);
}
printf("%s\n", Bellman_ford() ? "YES" : "NO");
}
}