生活随笔
收集整理的這篇文章主要介紹了
无向图——双连通分量
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
? ? 雙連通圖:在無向圖連通圖中,如果刪除該圖中的任意一點和依附它的邊,不改變圖的連通性,則稱該圖為雙連通的無向圖。
??? 由上述定義可知,雙連通分量中,每兩個結點之間至少有兩條不同的路徑可以相互到達。
??? 割點:在無向連通圖中刪去某個點a和依附a的邊,圖變為不連通,則該點稱為割點,也叫關節點。
??? 割邊:在無向連通圖中刪去某條邊,圖變為不連通,則該邊稱為割邊,也叫橋。
??? 點雙連通分支(塊)與邊雙連通分支:
點雙連通分支與邊雙連通分支是兩個完全不同的概念。割點可以存在多個點連通分支中(相反,橋就不一樣)。一個圖可以有割點而沒有割邊,也可以有割邊而沒有割點。
?
點雙連通分支的求法和邊雙連通分支的求法類似,不過在出棧的地方有些不同。下面介紹幾個例子。
?
POJ3177(3352)
題目大意:最少需要加多少條邊使得原圖變為雙連通圖(原圖連通)。
解:求橋(注意平行邊),在求橋的過程中縮點,利用并查集,將每個連通分量用一個點代表。將這些代表用橋連接起來,就構成了一顆樹。統計樹中度數為1的點(即葉子結點)的個數count,將葉子結點兩兩相連,則添加邊的數量為(count+1)/2。
Cpp代碼??
#include?<iostream>?? const?int?MAX?=?5002;?? ?? int?p[MAX];?? struct?Graph?? {?? ????int?to;?? ????int?next;?? }e[MAX*4];?? int?index[MAX];?? int?edgeNum;?? int?seq;?? ?? int?low[MAX];????????? int?dfn[MAX];????????? int?bridge[MAX][2],bridge_n;?? int?degree[MAX];?? int?n,m;?? ?? int?min(int?x,?int?y)?? {?? ????return?x?<?y???x?:?y;?? }?? ?? void?makeSet()?? {?? ????for(int?i?=?1;?i?<=?n;?i++)?? ????????p[i]?=?i;?? }?? ?? int?findSet(int?x)?? {?? ????if(x?!=?p[x])?? ????????p[x]?=?findSet(p[x]);?? ????return?p[x];?? }?? ?? void?Union(int?x,?int?y)?? {?? ????x?=?findSet(x);?? ????y?=?findSet(y);?? ????if(x?==?y)?? ????????return;?? ????p[y]?=?x;?? }?? ?? void?addEdge(int?from,?int?to)?? {?? ????e[edgeNum].to?=?to;?? ????e[edgeNum].next?=?index[from];?? ????index[from]?=?edgeNum++;?? ????e[edgeNum].to?=?from;?? ????e[edgeNum].next?=?index[to];?? ????index[to]?=?edgeNum++;?? }?? ?? ?? void?bridge_dfs(int?u,?int?v)?? {?? ????int?repeat?=?0;??????????????????? ????low[u]?=?dfn[u]?=?seq++;?? ????for(int?i?=?index[u];?i?!=?-1;?i?=?e[i].next)?? ????{?? ????????int?w?=?e[i].to;?? ????????if(w?==?v)?? ????????????repeat++;?? ????????if(dfn[w]?<?0)?? ????????{?? ????????????bridge_dfs(w,u);?? ????????????low[u]?=?min(low[u],low[w]);?? ????????????if(!(low[w]?>?dfn[u]))????????? ????????????{?? ????????????????Union(w,u);?? ????????????}?? ????????????else?? ????????????{?? ????????????????bridge[++bridge_n][0]?=?u;?? ????????????????bridge[bridge_n][1]?=?w;?? ????????????}?? ????????}?? ????????else?if(v?!=?w?||?repeat?!=?1)?????? ????????????low[u]?=?min(low[u],dfn[w]);?? ????}?? }?? ?? int?solve()?? {?? ????int?i,j;?? ????int?a,b;?? ????int?count?=?0;?? ????memset(degree,0,sizeof(degree));?? ????for(i?=?1;?i?<=?bridge_n;?i++)?? ????{?? ????????a?=?findSet(bridge[i][0]);?? ????????b?=?findSet(bridge[i][1]);?? ????????degree[a]++;?? ????????degree[b]++;?? ????}?? ????for(i?=?1;?i?<=?n;?i++)?? ????????if(degree[i]?==?1)?? ????????????count++;?? ????return?(count+1)/2;?? }?? ?? int?main()?? {?? ????int?i,j;?? ????int?a,b;?? ????edgeNum?=?0;?? ????seq?=?0;?? ????bridge_n?=?0;?? ????memset(index,-1,sizeof(index));?? ????memset(dfn,-1,sizeof(dfn));?? ????scanf("%d?%d",&n,&m);?? ????for(i?=?0;?i?<?m;?i++)?? ????{?? ????????scanf("%d?%d",&a,&b);?? ????????addEdge(a,b);?? ????}?? ????makeSet();?? ????bridge_dfs(1,-1);?? ????printf("%d\n",solve());?? ????return?0;?? }??
?
POJ2942
題目大意:有n個騎士,騎士一段時間要坐在圓桌上舉行高級會議,但要滿足條件:互相憎恨的騎士不能相鄰,圓桌上的人數必須是大于1的奇數。現在給出騎士之間的憎恨關系,問至少有多少個騎士要被排除在外。
解:首先建補圖,這樣騎士a和騎士b之間有連線,說明a和b可以相鄰。還可以想到奇環,不在任何奇環的騎士將被排除。問題是如何求圖中的奇環?這里有一個定理:雙連通分量中如果存在奇環,那么整個分量的點全部包含在奇環中(自己體會)。這樣,可以先求點的雙連通分量,判斷每個雙連通分量是否包含奇環(用染色,若相鄰點顏色相同,存在奇環),若存在奇環,則該分量包含的騎士都不會被排除。
Cpp代碼??
#include?<iostream>?? const?int?MAX?=?1001;?? int?n,m;?? ?? ?? bool?map[MAX][MAX];?? int?dfn[MAX],low[MAX],stack[MAX];?? int?top,seq,result;?? bool?b[MAX],used[MAX];?? int?color[MAX];?? ?? int?min(int?x,?int?y)?? {?? ????return?x?<?y???x?:?y;?? }?? ?? bool?isOk(int?v,?int?col)?? {?? ????color[v]?=?col;?? ????for(int?w?=?1;?w?<=?n;?w++)?? ????{?? ????????if(map[v][w])?? ????????{?? ????????????if(b[w])?? ????????????{?? ????????????????if(color[v]?==?color[w])?????? ????????????????????return?true;?? ????????????????if(color[w]?==?-1)?? ????????????????????isOk(w,?col^1);?? ????????????}?? ????????}?? ????}?? ????return?false;?? }?? ?? void?dummy(int?t,?int?*a)?? {?? ????int?i,j;?? ????memset(b,0,sizeof(b));???? ????for(i?=?0;?i?<?t;?i++)?? ????????b[a[i]]?=?true;?? ????for(i?=?0;?i?<?t;?i++)?? ????{?? ????????memset(color,-1,sizeof(color));?? ????????if(isOk(a[i],1))?? ????????????break;?? ????}?? ????if(i?<?t)?????????? ????{?? ????????for(j?=?0;?j?<?t;?j++)?? ????????{?? ????????????if(!used[a[j]])?? ????????????{?? ????????????????result++;?? ????????????????used[a[j]]?=?true;?? ????????????}?? ????????}?? ????}?? }?? ?? void?bicon(int?u)?? {?? ????int?a[MAX];?? ????low[u]?=?dfn[u]?=?seq++;?? ????stack[top]?=?u;?? ????top++;?? ????for(int?w?=?1;?w?<=?n;?w++)?? ????{?? ????????if(map[u][w])?? ????????{?? ????????????if(dfn[w]?<?0)????????????????????? ????????????{?? ????????????????bicon(w);?? ????????????????low[u]?=?min(low[u],low[w]);?? ????????????????if(low[w]?>=?dfn[u])??????? ????????????????{?? ????????????????????int?k?=?1;?? ????????????????????a[0]?=?u;?? ????????????????????do?? ????????????????????{?? ????????????????????????--top;?? ????????????????????????a[k++]?=?stack[top];?? ????????????????????}while(stack[top]?!=?w);?? ????????????????????dummy(k,a);?? ????????????????}?? ????????????}?? ????????????else?????????????????????? ????????????????low[u]?=?min(low[u],dfn[w]);?? ????????}?? ????}?? }?? ?? void?block()?? {?? ????for(int?i?=?1;?i?<=?n;?i++)?? ????????if(dfn[i]?<?0)?? ????????????bicon(i);?? }?? ?? int?main()?? {?? ????int?i,j;?? ????int?a,b;?? ????while(true)?? ????{?? ????????scanf("%d?%d",&n,&m);?? ????????if(n?==?0?&&?m?==?0)?? ????????????break;?? ????????memset(map,1,sizeof(map));?? ????????memset(dfn,-1,sizeof(dfn));?? ????????memset(used,0,sizeof(used));?? ????????seq?=?0;?? ????????top?=?0;?? ????????result?=?0;?? ????????for(i?=?0;?i?<?m;?i++)?? ????????{?? ????????????scanf("%d?%d",&a,&b);?? ????????????map[a][b]?=?false;?? ????????????map[b][a]?=?false;?? ????????}?? ????????for(i?=?1;?i?<=?n;?i++)?? ????????????map[i][i]?=?false;?? ????????block();?? ????????printf("%d\n",n?-?result);?? ????}?? ????return?0;?? }??
?
POJ3694
題目大意:給定一個初始的網絡,每次(1000次)向網絡里加一條邊,問網絡中橋的數量。(網絡是動態的)
解:這題難就難在網絡是動態的,如果是靜態,可以用邊的雙連通分量來直接求解。簡單的想法是每修改一次就重新計算一次,但是這樣超時。聯想:通過縮點,縮點之間用橋連接,形成一顆樹,樹邊就是橋,橋的總數為sum。每次向網絡里加一條邊a,b,先用并查集找出a和b所屬的樹的結點,顯然,a和b到ab的最近公共祖先這條路徑上的橋全部無效。這樣,每次只需在樹上操作sum--。這樣復雜度就降下來了。
Cpp代碼??
#include?<iostream>?? const?int?MAX?=?100002;?? int?n,m;?? int?result;?? struct?Edge?? {?? ????int?to;?? ????int?next;?? }e[MAX*10],tree[MAX*10];?? int?index[MAX],index2[MAX],edgeNum,edgeT;?? int?seq;?? ?? int?low[MAX],dfn[MAX];?? int?p[MAX],res[MAX];?? int?level[MAX],pre[MAX];?? bool?vis[MAX],bridge[MAX];?? ?? int?min(int?x,?int?y)?? {?? ????return?x?<?y???x?:?y;?? }?? ?? void?addEdge(int?from,?int?to)?? {?? ????e[edgeNum].to?=?to;?? ????e[edgeNum].next?=?index[from];?? ????index[from]?=?edgeNum++;?? }?? ?? void?addTree(int?from,?int?to)?? {?? ????tree[edgeT].to?=?to;?? ????tree[edgeT].next?=?index2[from];?? ????index2[from]?=?edgeT++;?? }?? ?? void?makeSet()?? {?? ????for(int?i?=?1;?i?<=?n;?i++)?? ????????p[i]?=?i;?? }?? ?? int?findSet(int?x)?? {?? ????if(x?!=?p[x])?? ????????p[x]?=?findSet(p[x]);?? ????return?p[x];?? }?? ?? void?Union(int?x,?int?y)?? {?? ????x?=?findSet(x);?? ????y?=?findSet(y);?? ????if(x?==?y)?? ????????return;?? ????p[x]?=?y;?? }?? ?? void?bridge_dfs(int?u,?int?v)?? {?? ????int?repeat?=?0;?? ????low[u]?=?dfn[u]?=?seq++;?? ????for(int?i?=?index[u];?i?!=?-1;?i?=?e[i].next)?? ????{?? ????????int?w?=?e[i].to;?? ????????if(v?==?w)?? ????????????repeat++;?? ????????if(dfn[w]?<?0)?? ????????{?? ????????????bridge_dfs(w,u);?? ????????????low[u]?=?min(low[u],low[w]);?? ????????????if(low[w]?>?dfn[u])?? ????????????{?? ????????????????result++;?? ????????????????res[result]?=?i;?? ?????????????????? ????????????}?? ????????????else?? ????????????????Union(w,u);?? ????????}?? ????????else?if(v?!=?w?||?repeat?!=?1)?? ????????????low[u]?=?min(low[u],dfn[w]);?? ????}?? }?? ?? void?lca_dfs(int?u,?int?deep)?? {?? ????for(int?i?=?index2[u];?i?!=?-1;?i?=?tree[i].next)?? ????{?? ????????int?v?=?tree[i].to;?? ????????if(!vis[v])?? ????????{?? ????????????vis[v]?=?true;?? ????????????pre[v]?=?u;?? ????????????level[v]?=?deep+1;?? ????????????lca_dfs(v,deep+1);?? ????????}?? ????}?? }?? ?? void?lca(int?u,?int?v)?? {?? ????while(level[u]?>?level[v])?? ????{?? ????????if(bridge[u])?? ????????{?? ????????????result--;?? ????????????bridge[u]?=?0;?? ????????}?? ????????u?=?pre[u];?? ????}?? ????while(level[v]?>?level[u])?? ????{?? ????????if(bridge[v])?? ????????{?? ????????????result--;?? ????????????bridge[v]?=?0;?? ????????}?? ????????v?=?pre[v];?? ????}?? ????while(u?!=?v)?? ????{?? ????????if(bridge[u])?? ????????{?? ????????????bridge[u]?=?0;?? ????????????result--;?? ????????}?? ????????if(bridge[v])?? ????????{?? ????????????bridge[v]?=?0;?? ????????????result--;?? ????????}?? ????????u?=?pre[u];?? ????????v?=?pre[v];?? ????}?? }?? ?? int?main()?? {?? ????int?i,j;?? ????int?a,b;?? ????int?q;?? ????int?cases?=?1;?? ????while(true)?? ????{?? ????????scanf("%d?%d",&n,&m);?? ????????if(n?==?0?&&?m?==?0)?? ????????????break;?? ????????edgeNum?=?0;?? ????????edgeT?=?0;?? ????????result?=?0;?? ????????seq?=?0;?? ????????memset(index,-1,sizeof(index));?? ????????memset(index2,-1,sizeof(index2));?? ????????memset(dfn,-1,sizeof(dfn));?? ????????memset(vis,0,sizeof(vis));?? ????????memset(level,0,sizeof(level));?? ????????memset(bridge,0,sizeof(bridge));?? ????????makeSet();?? ????????for(i?=?0;?i?<?m;?i++)?? ????????{?? ????????????scanf("%d?%d",&a,&b);?? ????????????addEdge(a,b);?? ????????????addEdge(b,a);?? ????????}?? ????????scanf("%d",&q);?? ????????printf("Case?%d:\n",cases++);?? ????????bridge_dfs(1,-1);????????? ????????int?x,y;?? ????????for(i?=?1;?i?<=?n;?i++)???? ????????{?? ????????????for(j?=?index[i];?j?!=?-1;?j?=?e[j].next)?? ????????????{?? ????????????????x?=?findSet(i);?? ????????????????y?=?findSet(e[j].to);?? ????????????????if(x?!=?y)?? ????????????????????addTree(x,y);?? ????????????}?? ????????}?? ?????????? ????????memset(vis,0,sizeof(vis));?? ????????vis[p[1]]?=?true;?? ????????level[p[1]]?=?1;?? ????????lca_dfs(p[1],1);?? ????????for(i?=?1;?i?<=?result;?i++)?? ????????????bridge[findSet(e[res[i]].to)]?=?1;?? ????????while(q--)?? ????????{?? ????????????scanf("%d?%d",&a,&b);?? ????????????a?=?findSet(a);?? ????????????b?=?findSet(b);?? ????????????if(a?!=?b)?? ????????????????lca(a,b);?? ????????????printf("%d\n",result);?? ????????}?? ????????printf("\n");?? ????}?? ????return?0;?? } ?
總結
以上是生活随笔為你收集整理的无向图——双连通分量的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。