给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。
请你判断图中是否存在负权回路。
输入格式
第一行包含整数 n 和 m。
接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z。
输出格式
如果图中存在负权回路,则输出 Yes
,否则输出 No
。
数据范围
1≤n≤2000
1≤m≤10000
图中涉及边长绝对值均不超过 10000。
1、我们可以将所有点存到队列中,开始循环,当对列为空时结束循环。
2、遍历到第i个节点的时候,将以 i 点为前驱结点(假设存在j)并且dist[i] + w[i到j] < dist[j]的所有节点都放入队列中(前提是这个点没有在队列中,st[]数组中有记录),并将这些点的步数改为上个节点 + 1。
3、如果节点的步数大于等于n,表明这个有向图存在负环,如果当队列为空时还没有步数大于等于n的点,则这个有向图没有负环。
- #include
- using namespace std;
- const int N = 100010;
-
- int n,m;// 输入n个点,m条边
- int h[N],e[N],ne[N],w[N],idx;// 邻接表四件套,外加w数组储存点a到点b的距离
- int dist[N],cnt[N];// dist储存距离初始值都为0,使用cnt记录到达当前点的时候是走的第几步。
- bool st[N];// 判断当前点i是否在队列q中
- queue<int> q;// 队列q
-
- void add(int a,int b, int c)// 建立邻接表,并将点a到点b的距离储存起来
- {
- w[idx] = c,e[idx] = b,ne[idx] = h[a],h[a] = idx ++;
- }
-
- bool spfa()
- {
- for(int i = 1; i <= n; i ++)// 将所有点都储存到队列q中(只从某一点开始,不一定能找到负环)
- {
- q.push(i);
- st[i] = true;
- }
- while(!q.empty())// 判断队列是否为空,如果为空就结束循环
- {
- int t = q.front();// 将q队列的队头赋值给t
- q.pop();// 将队列q的队头弹出
- st[t] = false;// 队列中已经没有t点,更新st数组
- for(int i = h[t]; i != -1; i = ne[i])// 使用这个点对与它连接的点进行更新
- {
- int j = e[i];
- if(dist[j] > dist[t] + w[i]) // 如果此点距离大于更新后的距离,则保留更新后的距离
- {
- dist[j] = dist[t] + w[i];// 更新这个点的距离,(如果更新这个点的距离,则表示此边为负权边)
- cnt[j] = cnt[t] + 1;// 如果负权边数量大于等于点的数量,则表示存在负环
- if(cnt[j] >= n) return true;
- if(!st[j])
- {
- q.push(j);
- st[j] = true;
- }
- }
- }
- }
- return false;
- }
-
- int main()
- {
- memset(h,-1,sizeof(h));// 将表头初始化
- cin >> n >> m;// 输入点数,边数
- for(int i = 0; i < m; i ++)// 输入边,以及两点之间的距离
- {
- int a,b,c;
- cin >> a >> b >> c;
- add(a,b,c);
- }
- if(spfa()) cout << "Yes" << endl;
- else cout << "No" << endl;
- return 0;
- }