1.求割点:
(1).朴素的方法:n^3
(2).Tarjan求割点:n^2
顶点u是割点的充要条件:
1.如果顶点u是深度优先搜索生成树的根,则u至少有两个子女.
2.如果u不是生成树的根,则它至少有一个子女w,从w出发,不可能通过w、w的子孙,以及一条回边组成的路径到达u的祖先.
(low[w]>=dfn[u]).
去掉割点,将原来的连通图分成了几个连通分量?
1.如果割点u是根结点,则有几个子女,就分成了几个连通分量.
2.如果割点u不是根结点,则有d个子女w,使得low[w]>=dfn[u],则去掉该结点,分成了d+1个连通分量。
下面是一段求割点的代码:
View Code
/*Source Code Problem: 1523 User: HEU_daoguang Memory: 4628K Time: 16MS Language: G++ Result: Accepted Source Code*/ #include <iostream> #include <stdio.h> #include <string.h> using namespace std; #define min(a,b) ((a)>(b)?(b):(a)); int Edge[1001][1001]; int visited[1001]; int nodes; int tmpdfn; int dfn[1001]; int low[1001]; int son; int subnets[1001]; void init() { low[1]=dfn[1]=1; tmpdfn=1; son=0; memset(visited,0,sizeof(visited)); visited[1]=1; memset(subnets,0,sizeof(subnets)); } void dfs(int u) { for(int v=1;v<=nodes;v++) { if(Edge[u][v]) { if(!visited[v]) { visited[v]=1; tmpdfn++; dfn[v]=low[v]=tmpdfn; dfs(v); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]) { if(u!=1) subnets[u]++; if(u==1) son++; } } else { low[u]=min(low[u],dfn[v]); } } } } int main() { int i; int u,v; int find; int number=1; while(1) { scanf("%d",&u); if(u==0) { break; } memset(Edge,0,sizeof(Edge)); nodes=0; scanf("%d",&v); if(u>nodes) nodes=v; if(v>nodes) nodes=u; Edge[u][v]=Edge[u][v]=1; while(1) { scanf("%d",&u); if(u==0) break; scanf("%d",&v); if(u>nodes) nodes=u; if(v>nodes) nodes=v; Edge[u][v]=Edge[v][u]=1; } if(number>1) printf("\n"); printf("Network #%d\n",number); number++; init(); dfs(1); if(son>1) subnets[1]=son-1; find=0; for(i=1;i<=nodes;i++) { if(subnets[i]) { find=1; printf(" SPF node %d leaves %d subnets\n",i,subnets[i]+1); } } if(!find) printf(" No SPF nodes\n"); } return 0; }
2.求双连通分量:
双连通分量特性:无割点,删除任何一点都不会影响其他点之间的通信。
双连通图:无割点的图.
下面是一段求双连通分量的代码:
View Code
#include <string.h> #include <iostream> #include <stdio.h> #define min(a,b) ((a)>(b)?(b):(a)) #define MAXN 20 #define MAXM 40 struct edge{ int u,v; }edges[MAXM]; int top; int Edge[MAXN][MAXN]; int vis[MAXN]; int n,m; int tmpdfn; int dfn[MAXN]; int low[MAXN]; void dfs(int u){ for(int v=1;v<=n;v++){ if(Edge[u][v]==1){ edge t; t.u=u; t.v=v; edges[++top]=t; Edge[u][v]=Edge[v][u]=2; if(!vis[v]){ vis[v]=1; tmpdfn++; dfn[v]=low[v]=tmpdfn; dfs(v); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]){ bool firstedge=true; while(1){ int flag=0; if(top<0) break; if(firstedge) firstedge=false; else printf(" "); printf("%d-%d",edges[top].u,edges[top].v); if((edges[top].u==u&&edges[top].v==v) || (edges[top].u==v&&edges[top].v==u)) flag=1; edges[top].u=0;edges[top].v=0; top--; if(flag) break; } printf("\n"); } } else low[u]=min(low[u],dfn[v]); } } } int main() { int u,v; int number=1; while(scanf("%d%d",&n,&m)!=EOF){ if(n==0 && m==0) break; memset(Edge,0,sizeof(Edge)); for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); Edge[u][v]=Edge[v][u]=1; } if(number>1) printf("\n"); number++; low[1]=dfn[1]=1; tmpdfn=1; memset(vis,0,sizeof(vis)); vis[1]=1; memset(edges,0,sizeof(edges)); top=-1; dfs(1); } return 0; } /* input 7 9 1 2 1 3 1 6 1 7 2 3 2 4 2 5 4 5 6 7 output 5-2 4-5 2-4 3-1 2-3 1-2 7-1 6-7 1-6 */
3.求顶点连通度k(G),求最小割顶集:
A,B为任意不相邻的两点.
P(A,B)为A-B之间的独立轨最大条数,最少要删除P(A,B)个点才能使A,B不连通.
求P(A,B)->构图+最大流
结论:一个图的任意A,B两点间的P(A,B)的最小值即为该图的顶点连通度k(G),即最小割顶集.
网络流求解:O(n^4)见代码:
View Code
/*Source Code Problem: 1966 User: HEU_daoguang Memory: 1056K Time: 16MS Language: G++ Result: Accepted Source Code*/ #include <iostream> #include <stdio.h> #include <string.h> using namespace std; #define V 300 #define E 30000 #define inf 0xffff int map[V][2]; int flag[V][V]; struct Edge { int u,v,c,next; }edge[E]; int n,m,cnt; int dist[V]; int head[V]; int que[V]; int sta[V]; int s,t; void init(){ cnt=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v,int c){ edge[cnt].u=u;edge[cnt].v=v;edge[cnt].c=c; edge[cnt].next=head[u];head[u]=cnt++; edge[cnt].u=v;edge[cnt].v=u;edge[cnt].c=0; edge[cnt].next=head[v];head[v]=cnt++; } int dinic(int s,int t){ int ans=0; while(true){ int left,right,u,v; memset(dist,-1,sizeof(dist)); left=right=0; que[right++]=s; dist[s]=0; while(left<right){ u=que[left++]; for(int k=head[u];k!=-1;k=edge[k].next){ u=edge[k].u; v=edge[k].v; if(edge[k].c > 0 && dist[v]==-1){ dist[v]=dist[u]+1; que[right++]=v; if(v==t){ left=right; break; } } } } if(dist[t]==-1) break; int top=0; int now=s; while(true){ if(now!=t){ int k; for(k=head[now];k!=-1;k=edge[k].next){ if(edge[k].c>0 && dist[edge[k].v]==dist[edge[k].u]+1) break; } if(k!=-1){ sta[top++]=k; now=edge[k].v; } else{ if(top==0) break; dist[edge[sta[--top]].v]=-1; now=edge[sta[top]].u; } } else{ int flow=inf,ebreak; for(int i=0;i<top;i++){ if(flow>edge[sta[i]].c){ flow=edge[sta[i]].c; ebreak=i; } } ans+=flow; for(int i=0;i<top;i++){ edge[sta[i]].c-=flow; edge[sta[i]^1].c+=flow; } now=edge[sta[ebreak]].u; top=ebreak; } } } return ans; } void build(int x,int y,int ver,int n,int m){ init(); for(int i=1;i<=n;i++){ addedge(i,i+n,1); } for(int i=0;i<m;i++){ addedge(map[i][0]+n,map[i][1],inf); addedge(map[i][1]+n,map[i][0],inf); } addedge(x,x+n,inf); addedge(y,y+n,inf); } int main() { //freopen("in.txt","r",stdin); while(scanf("%d%d",&n,&m)!=EOF){ int a,b; int ver=n*2+1; memset(map,0,sizeof(map)); memset(flag,0,sizeof(flag)); for(int i=0;i<m;i++){ scanf(" (%d,%d)",&a,&b); a++,b++; map[i][0]=a,map[i][1]=b; flag[a][b]=1; } if(m==0){ if(n==1) printf("1\n"); else printf("0\n"); continue; } int pre,ans=inf,sign=0; for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(!flag[i][j]){ sign=1; build(i,j,ver,n,m); ans=min(ans,dinic(i,j+n)); } } } if(sign){ printf("%d\n",ans); } else{ printf("%d\n",n); } } return 0; }
Stoer-Wagner算法:O(n^3),见博客L:
http://www.cnblogs.com/ylfdrib/archive/2010/08/17/1801784.html