题目概述
某人的农庄里有N个地点,编号1到N,地点之间有M条双向路,途径每条路需要一定时间,农庄里还有W个虫洞,虫洞都在各个地点里,虫洞是单向的,每次途径虫洞会使时间倒退一块,问该人从某点出发,可否令时间倒退到出发以前
时限
2000ms/6000ms
输入
第一行正整数times,其后times组数据,每组数据第一行正整数N,M,W,其后M行,每行三个整数,代表路连接的两个地点及其用时,其后W行,每行三个整数,代表虫洞起点终点及其倒流的时间
限制
1<=times<=5;1<=N<=500;1<=M<=2500;1<=W<=200;每条路用时<=10000;每个虫洞倒流时间<=10000
输出
每行一个字符串,若可以倒退,则为YES,否则为NO
样例输入
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
样例输出
NO
YES
讨论
图论,负权环判断,bellman_ford算法,不是队列优化的那个,这里用到了原版bellman_ford的优点,总是能在运行N-1轮后停下,因为每次都松弛了一个点,而最短路一共只有N个点,起点不用松弛,这一条是优化算法不具备的,因而统计运行次数,只要每次成功松弛了一个点,就可以再运行一次,只要运行次数到达N,就是有负权环
实现层面上,由于原版的是非常暴力的对每条边都松弛一次,因而根本不需要知道邻接表之类的,只要把所有边都存下,记好起点终点,每次遍历,松弛就可以了,不过需要注意方向的问题
分类的话……额不想单独开个新的分类了,就放到bellman_ford下吧
题解状态
200K,63MS,C++,1003B
题解代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
#define MAXN 504
#define memset0(a) memset(a,0,sizeof(a))
int N, M, W;//地点数 路数 虫洞数
int from[2704], to[2704], w[2704], dis[MAXN];//起点 终点(对路而言不分起点终点) 时间(虫洞是负的)2700=2500+200 最短路长度
bool fun()
{
for (int p = 0; p < M + W; p++)
scanf("%d%d%d", &from[p], &to[p], &w[p]);//input//虫洞和路一视同仁的读入
for (int p = 1; p <= N; p++)//初始化
dis[p] = INF;
dis[1] = 0;//起点不重要 哪个都一样
bool f = 1;//flag 标记本次是否有成功倒流 也就是是否已产生最短路
int cnt = 0;//运行次数
while (f) {
if (++cnt == N)//当运行第N次时就要停了
return 1;
f = 0;//没有倒流
for (int p = 0; p < M; p++) {//处理路的情况 双向
if (dis[to[p]]>dis[from[p]] + w[p])
dis[to[p]] = dis[from[p]] + w[p];
if (dis[from[p]]>dis[to[p]] + w[p])
dis[from[p]] = dis[to[p]] + w[p];
}
for (int p = M; p < M + W; p++)//处理虫洞的情况 单向
if (dis[to[p]]>dis[from[p]] - w[p]) {
dis[to[p]] = dis[from[p]] - w[p];
f = 1;//成功倒流
}
}
return 0;//
}
int main(void)
{
//freopen("vs_cin.txt", "r", stdin);
//freopen("vs_cout.txt", "w", stdout);
int times;
scanf("%d", ×);//input
while (times--) {
scanf("%d%d%d", &N, &M, &W);//input
if (fun())
printf("YES\n");//output
else
printf("NO\n");//output
}
}
EOF