标签:vector win solution time limit ber bre clu main
InputOn the first line one positive number: the number of testcases, at most 100. After that per testcase:
* One line containing two integers n (1 ≤ n ≤ 20000) and m (0 ≤ m ≤ 50000): the number of statements and the number of implications that have already been proved.
* m lines with two integers s1 and s2 (1 ≤ s1, s2 ≤ n and s1 ≠ s2) each, indicating that it has been proved that statement s1 implies statement s2.OutputPer testcase:
* One line with the minimum number of additional implications that need to be proved in order to prove that all statements are equivalent.Sample Input
2
4 0
3 2
1 2
1 3
Sample Output
4
2
本題大意:給出一個有向圖,問最少需要再加幾條邊才可以構成一個強連通圖。
解題思想:
用Tarjan算法把已有的強連通子圖找出來,看做一個點,這樣原圖就沒有強連通子圖了,接下來要做的就是把這些“點”連接成一個強連通圖。其實把它們看成一些鏈條,
這樣只要把它們的首尾相連串起來就可以得到一個強連通圖,那麼問題的根源就轉化成了找它們的首尾。在有向圖中找首尾,不就是拓撲排序嗎?
用拓撲排序的思想,尾就是入度為零的那些點,反過來,我們用出度來找出首,出度為零即可。
本題亦可作為Tarjan算法求聯通分量的模版,同時還有縮點的應用。
以下貼出代碼:
#include<bits/stdc++.h> #define N 50009 using namespace std; int n,m,cnt,tot,id; int dfn[N],low[N],in[N],num[N]; vector<int> mp[N]; stack<int> st; void ini() { for(int i=1;i<=n;i++) mp[i].clear(); tot=cnt=0; memset(dfn,0,sizeof(dfn)); } void dfs(int x)//Tarjan 算法 { dfn[x]=low[x]=++tot;//dfn是記的是第幾個被DFS搜到 st.push(x); //low是他的子圖能到達的最小的dfn的點的dfn值 in[x]=1; //放入棧中,說明正在處理 for(int i=0;i<mp[x].size();i++) { int t=mp[x][i]; if(!dfn[t]) //如果還沒被搜到,就往下搜 { dfs(t); low[x]=min(low[x],low[t]);//搜完出來的時候,把搜到的最小dfn穿上來 } else if(in[t]) low[x]=min(low[x],dfn[t]);//如果搜到被搜過的點,就取它的dfn值,必然比自己的小。 }//對本層的子節點的深搜結束 if(low[x]==dfn[x]) //如果它的子孫沒有到它的祖宗,那說明他的下面是跟上面不連通的 { cnt++; //跟祖宗不連通,那麼他就獨立的一個強聯通分量 while(1) { int t=st.top();//把處理過的節點從棧中取出,去掉標記 st.pop(); in[t]=0; num[t]=cnt; //把本次所有強連通的元素標記為一組,縮為一點 if(t==x) break; } } } int solve() { int r[N],c[N]; // 入度、出度 memset(r,0,sizeof(r)); memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) { if(!dfn[i]) dfs(i); } if(cnt==1) return 0; for(int i=1;i<=n;i++) { for(int j=0;j<mp[i].size();j++) { int v=mp[i][j]; //雖然這裡對每對點都計算入度出度并沒有問題 if(num[i]!=num[v]) //因為每對強連通分量之間必然只有一條邊相連 { //不然它們就不是獨立的強連通分量了 r[num[v]]++; c[num[i]]++; } } } int cntr=0,cntc=0; for(int i=1;i<=cnt;i++) { cntr+=(r[i]==0); //入度為零,即尾的個數 cntc+=(c[i]==0); //同理算出度,找首 } return max(cntr,cntc); //返回最大值,用最大值就可以把所有的首尾相連 } int main() { int T; scanf("%d",&T); while(T--) { ini(); scanf("%d%d",&n,&m); for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); mp[a].push_back(b); } printf("%d\n",solve()); } }
積累此種算法思想
HDU 2767 Proving Equivalences (Tarjan縮點)
标签:vector win solution time limit ber bre clu main
原文地址:https://www.cnblogs.com/Lin88/p/9490272.html