Luogu P3385 【模板】负环 - 题解
【模板】負環
題目描述
給定一個 nnn 個點的有向圖,請求出圖中是否存在從頂點 111 出發能到達的負環。
負環的定義是:一條邊權之和為負數的回路。
輸入格式
本題單測試點有多組測試數據。
輸入的第一行是一個整數 TTT,表示測試數據的組數。對于每組數據的格式如下:
第一行有兩個整數,分別表示圖的點數 nnn 和接下來給出邊信息的條數 mmm。
接下來 mmm 行,每行三個整數 u,v,wu, v, wu,v,w。
- 若 w≥0w \geq 0w≥0,則表示存在一條從 uuu 至 vvv 邊權為 www 的邊,還存在一條從 vvv 至 uuu 邊權為 www 的邊。
- 若 w<0w < 0w<0,則只表示存在一條從 uuu 至 vvv 邊權為 www 的邊。
輸出格式
對于每組數據,輸出一行一個字符串,若所求負環存在,則輸出 YES,否則輸出 NO。
樣例 #1
樣例輸入 #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
NO YES提示
數據規模與約定
對于全部的測試點,保證:
- 1≤n≤2×1031 \leq n \leq 2 \times 10^31≤n≤2×103,1≤m≤3×1031 \leq m \leq 3 \times 10^31≤m≤3×103。
- 1≤u,v≤n1 \leq u, v \leq n1≤u,v≤n,?104≤w≤104-10^4 \leq w \leq 10^4?104≤w≤104。
- 1≤T≤101 \leq T \leq 101≤T≤10。
提示
請注意,mmm 不是圖的邊數。
題解
題意
傳送門:link
給你 TTT 個圖,每個圖有 nnn 個點和 mmm 條由 uuu 到 vvv 邊權為 www 的邊。
現給你這 mmm 條邊,求圖中是否有負環。
做法
算法分析
根據題的大意我們可以看出,這是一道單源最短路的題目。
單源最短路算法有 dij 和 SPFA,那這道題應該用哪道算法呢?
我們看一下數據范圍:
-
1≤n≤2×1031 \leq n \leq 2 \times 10^31≤n≤2×103,1≤m≤3×1031 \leq m \leq 3 \times 10^31≤m≤3×103。
-
1≤u,v≤n1 \leq u, v \leq n1≤u,v≤n,?104≤w≤104-10^4 \leq w \leq 10^4?104≤w≤104。
-
1≤T≤101 \leq T \leq 101≤T≤10。
很明顯會有負環,這時 dij 就行不通了,我們就需要用 SPFA 了。
算法講解
SPFA 為能夠有效判斷負環的一種單源最短路算法,但其算法復雜度比不上 dij,在存在菊花圖的情況下,很容易原地爆炸。
但由于能夠判斷負環的性質,才使得它在同類最短路算法中沒有被淘汰(可應用于 Johnson 全源最短路算法中),其主要思想為貪心。
SPFA 的獨特性質在于他的一個點能夠不止一次進隊,這個特點有利也有弊,
利在于它可以通過入隊次數來判斷負環;
弊在于它容易被卡(關于SPFA,他死了)。
但在此題中它能完美勝任 dij(他不能判負環)。
算法實現
答題思路很簡單,先鏈式前向星存圖,再用 SPFA 遍歷即可,所以問題在于 SPFA 的整體框架。
收我們需要一個隊列,先把 111 號點存進去,再遍歷到與 111 號點所連接著的點,依次松弛、入隊,如果一個點入隊次數大于等于 nnn 就直接輸出 YES 接著 return;如果隊列為空了,但依舊沒有輸出 YES 就輸出 NO。
Code
源代碼 - C++
//注釋極少版 #include <iostream> #include <cstring> #include <cstdio> #include <queue>using namespace std; int t,cnt; int dis[10001],v[10001]; int head[10001],tim[10001];struct Node{int to,next,dis; }edge[100001];void add(int t1,int t2,int t3) {//存圖cnt++;edge[cnt].to=t2;edge[cnt].dis=t3;edge[cnt].next=head[t1];head[t1]=cnt; }void SPFA_dui(int n) {queue<int> q;memset(dis,0x3f,sizeof(dis));memset(v,0,sizeof(v));memset(tim,0,sizeof(tim));//初始化dis[1]=0;v[1]=1;q.push(1);//加入 1while (!q.empty()) {int x=q.front();q.pop();v[x]=0;for (int i=head[x];i;i=edge[i].next) {//鏈式前向星遍歷int y=edge[i].to;if (dis[y]>dis[x]+edge[i].dis) {//relaxdis[y]=dis[x]+edge[i].dis;tim[y]=tim[x]+1;if (tim[y]>=n) {//有負環cout<<"YES"<<endl;return;}if (!v[y]) {//入隊v[y]=1;q.push(y);}}}}cout<<"NO"<<endl;}int main() {scanf("%d",&t);for (int i=1;i<=t;i++) {memset(head,0,sizeof(head));//有多組數據,記得清空int n,m;scanf("%d%d",&n,&m);for (int j=1;j<=m;j++) {int t1,t2,t3;cin>>t1>>t2>>t3;add(t1,t2,t3);if (t3>=0) {add(t2,t1,t3);}}SPFA_dui(n);cnt=0;} }廣告\Huge{\bold{廣告}}廣告
綠樹公司官方博客\Huge{\bold{綠樹公司官方博客}}綠樹公司官方博客
lvshu.wss.cc\Huge{\bold{lvshu.wss.cc}}lvshu.wss.cc
總結
以上是生活随笔為你收集整理的Luogu P3385 【模板】负环 - 题解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【netlimiter】的使用
- 下一篇: 今天起,我公众号要改名了