hdu 3671 Boonie and Clyde
雙連通分量
題意:給一個無向圖,要求毀掉兩個點,使圖變得不連通,圖一開始是連通的
因為要毀掉兩個點,就不是簡單的求割點,再看看數據范圍,點數為1000,邊數為10000,Tarjan的時間復雜度為O(E),如果用枚舉法,先枚舉要毀掉的第一個點,再用Tarjan進行處理來找割點會不會超時呢?答案是不會,時間為O(v*E),剛好是千萬級別,不超
做法:先枚舉要刪除的第1個點,在原圖中刪除它,看看刪除它后整個圖的變化
? ? ? ? 1.整個圖變得不連通了(即這個點本身是割點),但是還沒完要分類討論一下
? ? ? ? (1).整個圖變為兩部分,但是兩部分剛好都是一個點,那么這兩個點再毀掉哪個點都好,圖的連通分支數都不會增加,這是一個特殊情況
例如,(1,2)(2,3)這種圖,是無解的,任意毀掉兩個點都無法增加圖的連通分支,所以方案數為0
? ? ? ? (2).整個圖分為兩部分,但是有一部分的點數為1,另一部分大于1,那么這時候只要在較大的那部分,任意毀掉一個點(無論是不是割點都行),最后整個圖都會至少被分為了兩個部分
? ? ? ? ? ? ? ? (如果毀掉的是割點,將分成更多份),所以這樣產生的方案數是V-2
? ? ? ? (3).整個圖分為了兩個部分,兩個部分的點數都大于1,那么任意在哪個部分毀掉那個點都可以(無論是不是割點都行),最后整個圖都會至少分為兩個部分,所以方案數為V-1
? ? ? ? (4).整個圖被分為了三個或更多的部分,那么也是在剩下的點中任意毀掉一個點都可以(無論那個點是不是割點),方案數為V-1
? ? ? ? ? ? ? (如果這個點剛好處于一個部分且這個部分只有它自己一個點,那么 ? ?毀掉后整個圖的分支數減1;如果這個點在一個部分且這個部分不止它一個點且這個點不是割點,那么分支數 ?不會增加,如果是割點分支數為增加)
? ? ?2.刪除第一個點后,整個圖還是連通的(是連通,不是雙連通)
? ? ? ? 那么就在剩下的圖中找割點,找到幾個,方案數就是多少
?
?
最后注意一點,這樣計算的結果,很容易想到是有重復的,但是不難想到,其實剛好重復了一次,因為對于一個圖,方案是固定的,枚舉了所有點,找出了所有已方案,相當于每個方案算了兩次,最后答案除2即可
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define N 1010 #define M 10010int n,tot,cancel,rts,num; int e[N][M]; int dfn[N],low[N],cut[N],iscut[N],cnum,dcnt;void dfs(int u ,int fa) {dfn[u] = low[u] = ++dcnt;++num;for(int i=1; i<=e[u][0]; i++){int v = e[u][i];if(v == cancel || v == fa) continue;if(!dfn[v]){dfs(v,u);low[u] = min(low[u] , low[v]);if(fa == -1) {rts++; continue;}if(dfn[u] <= low[v]){if(!iscut[u]){ iscut[u] = 1; cut[cnum++] = u;}}} else low[u] = min(low[u] , dfn[v]);} }void solve() {int res = 0,count,ok;for(cancel = 1; cancel<=n; cancel++) //枚舉第1個刪除的點 {count = cnum = 0;memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));memset(iscut,0,sizeof(iscut));ok = 0;for(int i=1; i<=n; i++)if(i!=cancel && !dfn[i]){count++;num = rts = 0;dfs(i , -1);if(num == 1) { ok++; continue; }if(rts >= 2) { iscut[i] = 1; cut[cnum++] = i; }}if(count >= 3) res += (n-1);else if(count == 2 && ok == 0) res += (n-1);else if(count == 2 && ok == 1) res += (n-2);else if(count == 1) res += cnum;}cout << res/2 << endl; }int main() {int cas = 0;while(cin >> n >> tot){if(!n && !tot) break;for(int i=1; i<=n; i++)e[i][0] = 0;while(tot--){int u , v , c;cin >> u >> v;c = ++e[u][0];e[u][c] = v;c = ++e[v][0];e[v][c] = u;}printf("Case %d: ",++cas);solve();}return 0; }?
轉載于:https://www.cnblogs.com/scau20110726/archive/2013/05/22/3092078.html
總結
以上是生活随笔為你收集整理的hdu 3671 Boonie and Clyde的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 依赖注入的实现
- 下一篇: Oracle数据文件的备份与恢复