洛谷-P3385 [模板] 负环¶
题目描述¶
暴力枚举 / SPFA / Bellman-ford / 奇怪的贪心 / 超神搜索
寻找一个从顶点 11 所能到达的负环,负环定义为:一个边权之和为负的环。
输入格式¶
本题有多组数据
第一行一个正整数 TT 表示数据组数,对于每组数据:
第一行两个正整数 N, MN,M,表示图有 NN 个顶点,MM 条边
接下来 MM 行,每行三个整数 a, b, wa,b,w,表示a \rightarrow ba→b 有一条权值为 ww 的边(若 w<0w<0 则为单向,否则双向)。
输出格式¶
共 TT 行。对于每组数据,存在负环则输出一行YE5
(不含引号),否则输出一行N0
(不含引号)。
输入输出样例¶
输入 #1
2
3 4
1 2 2
1 3 4
2 3 1
3 1 -3
3 3
1 2 3
2 3 4
3 1 -8
输出 #1
N0
YE5
说明/提示¶
n\le 2000n≤2000,m\le 3000m≤3000,-10000\le w \le 10000−10000≤w≤10000,T\le 10T≤10。
建议复制输出格式中的字符串。
本题数据感谢 @negiizhao 的精心构造,请不要使用玄学算法。
本题数据有更新。
#include <iostream>
#include <vector>
#include <cmath>
#include <string>
#include <climits>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
const int INF = 0X0ffffff;
struct Node
{
int from, to, cost;
};
int vertexNum = 2005, edgeNum = 6005;
vector<int> d(vertexNum, INT_MAX); //最短距离
vector<Node> es(edgeNum); //边
bool findNegativeCircle(int s)
{
fill(d.begin(), d.end(), INT_MAX);
d[s] = 0;
for (int i = 0; i < vertexNum; ++i) {
for (int j = 1; j <= edgeNum; ++j) {
if (d[es[j].from] != INT_MAX && d[es[j].to] > d[es[j].from] + es[j].cost) {
d[es[j].to] = d[es[j].from] + es[j].cost;
if (i == vertexNum - 1) return true;
}
}
}
return false;
}
int main()
{
std::ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int caseNum;
cin >> caseNum;
while (caseNum--) {
cin >> vertexNum >> edgeNum;
for (int i = 1; i <= edgeNum; ++i) {
cin >> es[i].from >> es[i].to >> es[i].cost;
if (es[i].cost >= 0) {
++edgeNum;
++i;
es[i].from = es[i - 1].to;
es[i].to = es[i - 1].from;
es[i].cost = es[i - 1].cost;
}
}
if (findNegativeCircle(1)) cout << "YE5" << endl;
else cout << "N0" << endl;
}
return 0;
}
这道题目核心考察是负环的检测,坑点有:
- 输出结果是
YE5
注意是数字5
,N0
是数字0
(最初提交进去发现就过了两个,其他全是WA,在于没有仔细看输出的要求) - 权值非负,是双向边,那么边的数量是增加的,所以最初给的边的数量是3000,但是很可能给的所有边的权重都是非负,那么就可能是6000条边,不然会出现RE(用邻接表就不会出现这种问题),所以空间开的大一点比较好。
上面这种用Bellman-Ford解题的方法,和《挑战程序设计竞赛》里不一样,如果初始d
内的值都是0会有一个测试用例过不去。