标签:c++
题目链接:
题意:
给出一张连通图,为了让任意两点都有两条通路(不能重边,可以重点),至少需要加多少条边
题解思路:
分析:在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。
缩点后,新图是一棵树,树的边就是原无向图桥。
现在问题转化为:在树中至少添加多少条边能使图变为双连通图。
结论:添加边数=(树中度为1的节点数+1)/2
具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。
代码:
#include<iostream> #include<cstdio> #include<cstring> #define maxn 5050 using namespace std; struct node { int to,tag,next; } edge[maxn*4]; int head[maxn]; int s; int dfn[maxn],low[maxn],num; int stack[maxn],top; int belong[maxn],block; //连通块 void init() { memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); block=num=top=s=0; } void addedge(int a,int b) { edge[s]= {b,0,head[a]}; head[a]=s++; } void Tarjan(int u,int pre) { dfn[u]=low[u]=++num; stack[top++]=u; for(int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].to; if(!dfn[v]) { Tarjan(v,u); low[u]=min(low[u],low[v]); if(dfn[u]<low[v]) { edge[i].tag=1; // edge[i^1].tag=1; //标记割边 } } else if(pre!=v) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) //找到一个边双连通分量的根节点 { int d; block++; while(1) { d=stack[--top]; belong[d]=block; //缩点 类似并查集的father[] if(d==u) break; } } } int main() { int degree[maxn]; int ans; int n,m,a,b; while(~scanf("%d%d",&n,&m)) { init(); while(m--) { scanf("%d%d",&a,&b); addedge(a,b); addedge(b,a); } Tarjan(1,-1); memset(degree,0,sizeof(degree)); ans=0; for(int i=1; i<=n; i++) //缩点后 不需要构图 只需要判断顶点度数即可 { for(int j=head[i]; j!=-1; j=edge[j].next) if(edge[j].tag==1) //缩点后只有割边才能成为这些"点"的边 degree[belong[i]]++; } for(int i=1; i<=block; i++) if(degree[i]==1) ans++; cout<<(ans+1)/2<<endl; } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
POJ 3177 Redundant Paths 边双连通分量+缩点
标签:c++
原文地址:http://blog.csdn.net/axuan_k/article/details/46744687