题解 UVA11721 Instant View of Big Bang(SPFA建反图判负环)
题意
给出一张n (\(n\leq1000\)) 点m (\(m\leq2000\)) 边的有向图,要求判断负环,并升序输出可以到负环的点
(多组数据 \(T\leq125\));
算法
建反图 + SPFA判负环 + dfs染色
思路
当建完反图后,负环是不变的,原图上所有能到负环的点在反图上变成了负环能到的点,因此只需找出负环上的点,dfs染色即可
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e4 + 10;
int n,m,head[maxn],num,flag,dis[maxn],vis[maxn],mark[maxn],cnt[maxn];
struct Edge{
int then,to,val;
}e[maxn];
void add(int u, int v, int val){e[++num] = (Edge){head[u], v, val}; head[u] = num;}
void dfs(int p){
mark[p] = 1;
for(int i = head[p]; i; i = e[i].then){
int v = e[i].to;
if(!mark[v]) dfs(v);
}
}
void SPFA(int st){
queue<int> q;
memset(dis,0x3f3f3f3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
dis[st] = 0; vis[st] = 1; q.push(st);
while(!q.empty()){
int u = q.front();
q.pop(); vis[u] = 0;
for(int i = head[u]; i; i = e[i].then){
int v = e[i].to;
if(mark[v]) continue;
if(dis[v] > dis[u] + e[i].val){
dis[v] = dis[u] + e[i].val;
if(!vis[v]){
cnt[v] ++;
if(cnt[v] > n) flag = 1, dfs(v);
//注意:这里是 >n 而不是 >=n 例如:只有一个点时
q.push(v);
vis[v] = 1;
}
}
}
}
}
int main(){
int T;
scanf("%d", &T);
for(int t = 1; t <= T; ++ t){
num = 0; flag = 0;
memset(head,0,sizeof(head));
memset(mark,0,sizeof(mark));
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++ i){
int u,v,val;
scanf("%d%d%d", &u, &v, &val);
add(v, u, val);
}
for(int i = 0; i < n; ++ i) add(n, i, 0);
SPFA(n);
printf("Case %d:", t);
if(!flag) printf(" impossible\n"); //注意空格,换行
else{
for(int i = 0; i < n; ++ i)
if(mark[i]) printf(" %d", i);
printf("\n");
}
}
return 0;
}