点双联通分量:没有割点;
边双联通分量:没有割边;
如何求无向图割点?
dfs建树解决;
low数组表示可到达祖先,num表示递归深度;
如果 low[v]>=num[u],说明是割点;
如果 low[v]>num[u] 说明是割边;
如果 num[v]<num[u]&&v!=fa,说明是回退边,
Network
POJ - 1144给你一个无向图,求割点;
#include<iostream> #include<vector> #include<cstdio> using namespace std; #define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++) #define per(i,j,k) for(int i=(int)k;i>=(int)j;i--) #define pb push_back #define pf push_front #define fi first #define se second 11 typedef long long ll; typedef unsigned long long ull; typedef long double ldb; typedef double db; const ll INF=0x3f3f3f3f3f3f3f3fLL; const int inf=0x3f3f3f3f;//0x7fffffff; const double eps=1e-9; const ll MOD=9999991; const int maxn=1e2+5; int n; vector<int>e[maxn]; bool iscut[maxn]; int low[maxn],dep[maxn],dfn; void dfs(int u,int fa){ low[u]=dep[u]=++dfn; int child=0; for(int i=0;i<e[u].size();i++){ int v=e[u][i]; if(!dep[v]){ child++; dfs(v,u); low[u]=min(low[u],low[v]); if(low[v]>=dep[u]&&u!=1)iscut[u]=1; } else if(dep[v]<dep[u]&&v!=fa) low[u]=min(low[u],dep[v]); } if(u==1&&child>=2)iscut[1]=1; } int main(){ while(~scanf("%d",&n),n){ for(int i=1;i<=n;i++)e[i].clear(),iscut[i]=0,low[i]=0,dep[i]=0; int u,v; while(~scanf("%d",&u),u){ while(getchar()!=' '){ scanf("%d",&v); e[u].pb(v); e[v].pb(u); } } dfn=0; dfs(1,1); int ans=0; for(int i=1;i<=n;i++)ans+=iscut[i]; // cout<<"ans:"<<ans<<endl; cout<<ans<<endl; } // system("pause"); return 0; }
Road Construction
给你一个无向图:问你最少加几条边,使得图成为双联通分量 ;
low相同的为一个联通块,遍历一遍,然后缩点,
求出答案;
#include<iostream> #include<vector> #include<cstdio> using namespace std; #define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++) #define per(i,j,k) for(int i=(int)k;i>=(int)j;i--) #define pb push_back #define pf push_front #define fi first #define se second 11 typedef long long ll; typedef unsigned long long ull; typedef long double ldb; typedef double db; const ll INF=0x3f3f3f3f3f3f3f3fLL; const int inf=0x3f3f3f3f;//0x7fffffff; const double eps=1e-9; const ll MOD=9999991; const int maxn=1e3+5; int dfn,n,m; int degree[maxn],low[maxn]; vector<int>e[maxn]; void dfs(int u,int fa){ low[u]=++dfn; for(int i=0;i<e[u].size();i++){ int v=e[u][i]; if(v==fa)continue; if(!low[v])dfs(v,u); low[u]=min(low[u],low[v]); } } int tarjan(){ for(int i=1;i<=n;i++){ for(int j=0;j<e[i].size();j++){ if(low[i]!=low[e[i][j]])degree[low[i]]++; } } int res=0; for(int i=1;i<=n;i++)if(degree[i]==1)res++; return res; } int main(){ while(~scanf("%d %d",&n,&m)){ for(int i=0;i<=n;i++)degree[i]=0,e[i].clear(),low[i]=0; while(m--){ int a,b; scanf("%d %d",&a,&b); e[a].pb(b); e[b].pb(a); } dfn=0; dfs(1,-1); int ans=tarjan(); cout<<(ans+1)/2<<endl; } return 0; }
Railway
题意:给你一张图;
求桥的个数,联通分量有多个圈,圈里的边的数量;
dfs解决桥的个数,然后遇到割点,说明构成一个联通块,统计一下这个联通块的点数和边数;
#include<bits/stdc++.h> using namespace std; #define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++) #define per(i,j,k) for(int i=(int)k;i>=(int)j;i--) #define pb push_back #define pf push_front #define fi first #define se second 11 typedef long long ll; typedef unsigned long long ull; typedef long double ldb; typedef double db; const db PI=acos(-1.0); const ll INF=0x3f3f3f3f3f3f3f3fLL; const int inf=0x3f3f3f3f;//0x7fffffff; const double eps=1e-9; const ll MOD=9999991; const int maxn=1e4+5; int n,m,dfn,bridge,cnt; struct edge{int u,v;edge(int a,int b){u=a,v=b;}}; vector<edge>e[maxn]; int num[maxn],low[maxn]; stack<edge>stk; set<int>point; int ans; void dfs(int u,int fa){ num[u]=low[u]=++dfn; for(int i=0;i<e[u].size();i++){ int v=e[u][i].v; if(!num[v]){ stk.push(e[u][i]); dfs(v,u); low[u]=min(low[u],low[v]); if(low[v]>=num[u]){ edge tmp(edge(1,1)); int cnt=0; point.clear(); do { cnt++; tmp=stk.top(); stk.pop(); point.insert(tmp.u); point.insert(tmp.v); }while(tmp.u!=u||tmp.v!=v); if(cnt>point.size())ans+=cnt; } if(low[v]>num[u])bridge++; } else if(num[v]<num[u]&&v!=fa){ stk.push(e[u][i]); low[u]=min(low[u],num[v]); } } } int main(){ while(~scanf("%d %d",&n,&m),n+m){ while(!stk.empty())stk.pop(); for(int i=0;i<=n;i++)e[i].clear(),num[i]=0,low[i]=0; while(m--){ int a,b; scanf("%d %d",&a,&b); e[a].pb(edge(a,b)); e[b].pb(edge(b,a)); } dfn=0,bridge=0,ans=0; // dfs(1,-1); for(int i=1;i<=n;i++) if(!num[i])dfs(i,-1); // cout<<"test"<<endl; cout<<bridge<<" "<<ans<<endl; } // system("pause"); return 0; }
Financial Crisis
题意:给你一个无向图,问你两个点,之间有几条路,
没有路:不连通,用并查集解决;
两条以上:在a,b,在一个点双联通分量里,并且,这个联通分量的点数要大于2;
剩下的就是一条;
解法:判断是不是在一个点联通分量,只需要记一下low数组,相同就在一个联通分量上,但麻烦的是你要记录这个联通分量。
做法:dfs求深度优先生成树,然后遇到割点就把联通分量求出来,注意判重;
#include<bits/stdc++.h> using namespace std; #define rep(i,j,k) for(int i=(int)j;i<=(int)k;i++) #define per(i,j,k) for(int i=(int)k;i>=(int)j;i--) #define pb push_back #define pf push_front #define fi first #define se second 11 typedef long long ll; typedef unsigned long long ull; typedef long double ldb; typedef double db; const db PI=acos(-1.0); const ll INF=0x3f3f3f3f3f3f3f3fLL; const int inf=0x3f3f3f3f;//0x7fffffff; const double eps=1e-9; const ll MOD=9999991; const int maxn=5e3+5; int fa[maxn]; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void build(int x,int y){int dx=find(x),dy=find(y);if(dx!=dy)fa[dx]=dy;} int n,m,q,dfn,bcnt; struct edge{int u,v;edge(int a,int b){u=a,v=b;}}; vector<edge>e[maxn]; int low[maxn],dep[maxn],color[maxn]; vector<int>belong[maxn]; stack<edge>stk; vector<int>bcc[maxn]; void dfs(int u,int fa){ dep[u]=low[u]=++dfn; for(int i=0;i<e[u].size();i++){ int v=e[u][i].v; if(v==fa)continue; if(!low[v]){ stk.push(e[u][i]); dfs(v,u); low[u]=min(low[u],low[v]); if(low[v]>=dep[u]){ bcnt++; bcc[bcnt].clear(); edge tmp(edge(1,1)); while(1){ tmp=stk.top(); stk.pop(); int x=tmp.u,y=tmp.v; if(color[x]!=bcnt){ color[x]=bcnt,bcc[bcnt].pb(x),belong[x].pb(bcnt); } if(color[y]!=bcnt){ color[y]=bcnt,bcc[bcnt].pb(y),belong[y].pb(bcnt); } if(x==u&&y==v)break; } } } else if(dep[v]<dep[u]&&v!=fa)low[u]=min(low[u],dep[v]); } } int main(){ int cas=1; while(~scanf("%d %d %d",&n,&m,&q),n+m+q){ dfn=0,bcnt=0; for(int i=0;i<n;i++)dep[i]=0,color[i]=0,belong[i].clear(),e[i].clear(),fa[i]=i,low[i]=0; while(m--){ int a,b; scanf("%d %d",&a,&b); e[a].pb(edge(a,b)); e[b].pb(edge(b,a)); build(a,b); } for(int i=0;i<n;i++)if(!low[i])dfs(i,-1); printf("Case %d: ",cas++); while(q--){ int a,b; scanf("%d %d",&a,&b); if(find(a)!=find(b))cout<<"zero"<<endl; else { int flag=0; for(int i=0;i<belong[a].size()&&!flag;i++) for(int j=0;j<belong[b].size()&&!flag;j++){ if(belong[a][i]==belong[b][j]){ int num=belong[a][i]; if(bcc[num].size()>2)flag=1,cout<<"two or more"<<endl; } } if(!flag)cout<<"one"<<endl; } } } // system("pause"); return 0; }