边双连通

边双连通
边双连通分量(图):去掉任一边,剩下点依然连通……每个点度数都要为2,不存在桥
点双连通分量(图):去掉任一点,乘下点依然连通……不存在割点

poj3352(边双连通分量)
题意:给一个无向连通图,至少添加几条边使得去掉图中任意一条边不改变图的连通性(即使得它变为边双连通图)。
思路:tarjan求双连通分量,然后缩点,缩点后的图是一个树形图,在其中寻找度为1的节点,设有x个,答案就是(x+1)/2。
Sample Input 1
10 12
1 2
1 3
1 4
2 5
2 6
5 6
3 7
3 8
7 8
4 9
4 10
9 10
Sample Input 2
3 3
1 2
2 3
1 3
Sample Output
Output for Sample Input 1
2
Output for Sample Input 2
0

#include
using namespace std;
const int N=1010;
struct data{int to,next;} tu[N*N];
int n,m;
int head[N],low[N],dfn[N];//dfn[]记录某个点被访问到的步数序号,low[]记录某个点或其子树回边的最小步数的点
int cut[N];//0表示该点不为割点,>0表示该点连通的子图个数
int sta[N];
int top;
int ip;
int step,rt_son;
int bridge;
int degree[N];
int cmp[N];
int suodian_cnt;
void init(){
	suodian_cnt=0;
	top=0;
    ip=0;
    step=1;//遍历的步数
    rt_son=0;//根的孩子数量,在本例中无作用
    bridge=0;
    memset(head,-1,sizeof(head));
    memset(dfn, 0, sizeof(dfn));
    memset(cut, 0, sizeof(cut));
    memset(degree,0,sizeof(degree));
    memset(cmp,0,sizeof(cmp));
}
void add(int u,int v){tu[ip].to=v,tu[ip].next=head[u],head[u]=ip++;}
void tarjan(int u,int fa){//当前点及其老豆
    dfn[u] = low[u] = step++;//刚进入时就理更新为当前时间chuo
    sta[++top]=u;//入栈
    for(int i = head[u]; i!=-1 ; i=tu[i].next){ //访问以u为弧头的边进行深搜,目的就是更新当前点的LOW
        int to = tu[i].to;//读出指向
        if(!dfn[to]){//表示未被访问的点
            tarjan(to,u);//深搜
            low[u]=min(low[u],low[to]);//然后更新当前点的子树回边的最小步数的点
            if(low[to]>dfn[u])//如果他之后的点不能够回到他以前,当前的I边就是一条桥,由U指向TO
                bridge++;//桥数加1,在本例中无作用,只是顺便把桥数计了
        }
        else  if(to!=fa)//如果已经访问
            low[u]=min(low[u],dfn[to]);//直接更新当前LOW即可
    }
    if(low[u]==dfn[u]){//深完当前点的所有边后就要由DFN与LOW判连通量的根,LOW==DFN表示他是割点
    	suodian_cnt++;//每次进来就表示多一个缩点
        while(top){//不断出栈
            cmp[sta[top]]=suodian_cnt;//当前栈顶点归入当前suodian_cnt的缩点集中
            top--;//退栈
            if(sta[top+1]==u)break;//注意跳出条件,top+1表示刚出栈的那个点就是u,就可以停止
        }
	}
}
void solve(){
	for(int u=1;u<=n;u++){//逐个点扫一次
		for(int j=head[u];j!=-1;j=tu[j].next){//访问以u为弧头的边
        	int to = tu[j].to;//读出射向的点
	        if(cmp[to]!=cmp[u]){//不在同一集合连一条双向边,什么意思?TARGAN分好了缩点点集
				degree[cmp[u]]++;//注意了,因为是双向边,现在判U->TO,则TO->U是会重判
				degree[cmp[to]]++;//所以缩点后连边的度是翻倍的
			}
		}
	}
	int ans=0;
	for(int i=1;i<=suodian_cnt;i++)	{//扫一次缩点后的图的全部点
		if(degree[i]==2)ans++;//寻找度为1的点(在上面扫的时候翻倍了)
	}
	printf("%d\n",(ans+1)/2);//易证,列几个点数一下即可,奇偶皆付合
}
int main(){
	while(~scanf("%d%d",&n,&m)){//N个点M条边
		init();
		int u,v;
		for(int i=0;i

 

你可能感兴趣的:(ACM笔记-3图流)