首页 > 学院 > 开发设计 > 正文

POJ 3352 Road Construction(边双连通)

2019-11-06 09:19:28
字体:
来源:转载
供稿:网友

POJ 3352

题目大意

一个有 N 个景点的岛,任意两个景点都有道路相连,当道路施工时,游客便不能在该道路上通行,问至少再增加几条道路可以使得在任一条道路维修的情况下,游客都能从岛上任意一个景点到达另一个景点。

分析

重述一下问题也就是问“至少增加几条边能使一个无向图变成边双连通”

如果将各个边双连通分量都缩成一个点,那么整个图就变成了一颗树

要使得一棵树变为一个双连通图,有一个定理:

增加的边数 = (树中总度数为1的节点数+1)/ 2

这个公式的解释:“简单说明一下,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。”(转自byvoid)

总结

一开始是在《ACM-ICPC 培训资料汇编》例题里面看见这道题的,发现里面的代码只用了low一个数组,感觉好神奇,然而想了很久始终想不通为什么这样是对的,造了一组数据这方法很明显不对吧,然后网上看博客后一部分人也是这种方法过的,数据太水了?,错误的方法也能AC?。还是老老实实地去学tarjan的标准写法吧。

每找到一个边双连通分量就出栈。

代码

#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<cstdlib>#include<queue>#include<map>#include<algorithm>#include<set>#include<stack>using namespace std;const int MAXN=1005;int bj[MAXN][MAXN];struct node //前向星实现{ int v; int next;}edge[10005];int edgecount;int head[MAXN];int ti=0;//时间int dfn[MAXN];int low[MAXN];int vis[MAXN];int belong[MAXN];int de[MAXN];int n,m;int cnt;void Add_edge(int u,int v){ edge[++edgecount].v=v; edge[edgecount].next=head[u]; head[u]=edgecount;}void Init(){ memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(vis,0,sizeof(vis)); memset(de,0,sizeof(de)); edgecount=0; ti=0; cnt=0;}stack<int> St;void Tarjan(int p,int u){ dfn[u]=low[u]=++ti; St.push(u); vis[u]=1; for(int k=head[u];k!=-1;k=edge[k].next) { int v=edge[k].v; if(v==p)continue; if(!vis[v]) { Tarjan(u,v); low[u]=min(low[u],low[v]); } else// if(vis[v]==1)//v在栈中,说明(u,v)是返祖边 { low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u])//将u所在的边连通分量出栈 { int x; ++cnt;//cnt表示缩点后的连通分量 while(1) { x=St.top(); St.pop(); vis[x]=0; belong[x]=cnt; if(x==u)break; } }}void Solve(){ for(int i=1;i<=n;i++) { if(dfn[i]==0)Tarjan(0,i); } for(int u=1;u<=n;u++) { for(int k=head[u];k!=-1;k=edge[k].next) { int v=edge[k].v; if(belong[u]!=belong[v]) de[belong[u]]++; } } int ans=0; for(int i=1;i<=cnt;i++) { if(de[i]==1)ans++; } ans=(ans+1)/2; cout<<ans<<endl;}int main(){ int a,b; while(scanf("%d%d",&n,&m)!=EOF) { Init(); for(int i=1;i<=m;i++) { scanf("%d%d",&a,&b); Add_edge(a,b); Add_edge(b,a); } Solve(); } return 0;}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表