1. 程式人生 > >2017 烏魯木齊賽區網路賽 Islands(【點強連通問題】【縮點+點強連通分量】)

2017 烏魯木齊賽區網路賽 Islands(【點強連通問題】【縮點+點強連通分量】)

題目連結:https://nanti.jisuanke.com/t/16955
【中文題意】給你一個有向圖,然後讓你加最少的邊使得全圖強連通(即從任意一個點出發,可以到達剩餘的所有點)。
【思路分析】非常經典的板子題。先找強連通分量,然後縮點後求邊的條數。
【AC程式碼】

#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
using namespace std; #define maxn 10005 vector<int>G[maxn]; int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt,n,m; stack<int>S; void dfs(int u) { pre[u] = lowlink[u] = ++dfs_clock; S.push(u); for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if
(!pre[v]) { dfs(v); lowlink[u] = min(lowlink[u],lowlink[v]); } else if(!sccno[v]) { lowlink[u]=min(lowlink[u],pre[v]); } } if(lowlink[u]==pre[u]) { scc_cnt++; for(;;) { int x=S.top(); S.pop(); sccno[x]=scc_cnt; if
(x==u)break; } } } void find_scc(int n) { dfs_clock = scc_cnt = 0; memset(sccno, 0, sizeof(sccno)); memset(pre,0,sizeof(pre)); for(int i = 0; i < n ; i++) { if(!pre[i])dfs(i); } } int in[maxn],out[maxn]; int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=0; i<n; i++)G[i].clear(); for(int i=0; i<m; i++) { int u,v; scanf("%d%d",&u,&v); u--; v--; G[u].push_back(v); } find_scc(n); //printf("%d***\n",scc_cnt); /*for(int i=0; i<n; i++) { printf("%d+++\n",sccno[i]); }*/ for(int i=1; i<=scc_cnt; i++) { in[i]=out[i]=1; } for(int u=0; u<n; u++) { for(int i=0; i<G[u].size(); i++) { int v=G[u][i]; if(sccno[u]!=sccno[v]) { in[sccno[v]]=out[sccno[u]]=0; } } } int a = 0,b=0; for(int i=1; i<=scc_cnt; i++) { if(in[i])a++; if(out[i])b++; } int ans = max(a,b); if(scc_cnt==1) { printf("0\n"); } else { printf("%d\n",ans); } } return 0; }