[kuangbin带你飞]专题九 连通图E POJ 3177 Redundant Paths
这个题最开始我想的是,直接缩点求双连通分量,连接这些双联通分量不就行了吗?
但是其实是不对的,双连通内部双联通,我们如果任意的连接一条边在这些双联通分量之间,他们之间有没有桥其实并不知道。
我应该是求缩点以后的叶子节点的个数,因为叶子节对于其本身来说,只有一条桥于其相连,我们可以连接两个叶子节点。那么这个两个区域就合成了一个双联通分量区域。同时为了减少边的使用,我们可以连接两个都是叶子节点的点。最后+1,剩下一个也应该连接。这样就能非常容易的算出把图变成双连通,所需要的边的数目。
#include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> using namespace std; const int N = 1e4+5,M = 1e5+5; int head[N],Next[M],ver[M],low[M],dfn[M],stack[M]; int block; int belong[M],deg[M]; bool brige[M],ins[M]; int c[M],dcc,top; int tot,n,m,num,cnt; void add(int x,int y) { ver[++tot]=y; Next[tot]=head[x]; head[x]=tot; } void tarjan(int u,int pre) { int v; dfn[u]=low[u]=++num; stack[++top]=u; ins[u]=1; for (int i=head[u]; i; i=Next[i]) { v=ver[i]; if (v==pre)continue; if (!dfn[v]) { tarjan(v,u); low[u]=min(low[u],low[v]); if (low[v]>dfn[u]) { brige[i]=1;//标记为桥 brige[i^1]=1; cnt++; } } else low[u]=min(low[u],dfn[v]); } if (low[u]==dfn[u]){//如果当前节点是一个根节点 也就是说我们DFS完了这个连通分量 cnt++; do { v=stack[top--];//把这个连通分量里面的所有点都拿出来 ins[v]=0; c[v]=cnt;//标记上所在的连通分量 }while(u!=v); } } void init(){ memset(Next,0,sizeof(Next)); memset(low,0,sizeof(low)); memset(brige,0,sizeof(brige)); memset(head,0,sizeof(head)); memset(ver,0,sizeof(ver)); memset(dfn,0,sizeof(dfn)); memset(c,0,sizeof(c)); tot=1; cnt=0; num=0; dcc=0; top=0; } int main() { int u,v; while(~scanf("%d%d",&n,&m)) { init(); for (int i=1; i<=m; i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } for (int i=1;i<=n;i++){ if (!dfn[i]){ tarjan(i,i); } } for (int i=1;i<=n;i++){ for (int j=head[i];j;j=Next[j]) { if (brige[j]) //缩点后把桥两边的缩点的度++ deg[c[i]]++; } } int ans=0; for (int i=1;i<=cnt;i++){ if (deg[i]==1){//我们找出缩点后度为1的点,也就是叶子节点, ans++; } } printf("%d\n",(ans+1)/2);//那么把图连接成双联通分量的所需边=(缩点后叶子节点的个数+1)/2 //因为很简单 我把叶子节点两两连接起来,那么这样消耗的边也就是最少的。这样我们就能让图 //变成双联通的 } return 0; }
有不懂欢迎咨询
QQ:1326487164(添加时记得备注)