联通分量

点双联通分量:没有割点;

边双联通分量:没有割边;

如何求无向图割点?

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;
}
View Code

Road Construction

 POJ - 3352 

给你一个无向图:问你最少加几条边,使得图成为双联通分量 ;

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;
}
View Code

Railway

 HDU - 3394 

题意:给你一张图;

求桥的个数,联通分量有多个圈,圈里的边的数量;

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;
}
View Code

Financial Crisis

 HDU - 3749 

题意:给你一个无向图,问你两个点,之间有几条路,

没有路:不连通,用并查集解决;

两条以上:在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;
}
View Code
想的太多,做的太少;
原文地址:https://www.cnblogs.com/littlerita/p/12405395.html