双连通分量 poj3352 poj2186
来源:互联网 发布:mac恢复数据 编辑:程序博客网 时间:2024/05/17 01:24
今天一共a了两个双连通分量的题,但是,真是马虎啊,一个runtime error了好长时间,一个是没有考虑周全然后wa了好长时间,然后看了一下别人的代码就明白了,虽然没有找测试数据,但是真的还是木有成就感啊!
poj3352:
大致题意:给出m个n个牛之间的崇拜关系,并且崇拜关系是可以传递的,让求可以受到任何牛都崇拜的牛的个数!
算法框架:对有向图求强连通分量,然后找出所有独立的强连通分量(所谓独立,就是该连通分量里面的点到外面的点没有通路,当然,连通分量外的点是可以有路到强连通分量内的点的),如果独立的强连通分量的数目只有一个,那么,就输出这个强连通分量内解的个数,否则输出无解。
算法证明:
1:假设a和b都是最受欢迎的cow,那么,a欢迎b,而且b欢迎a,于是,a和b是属于同一个连通分量内的点,所有,问题的解集构成一个强连通分量。
2:如果某个强连通分量内的点a到强连通分量外的点b有通路,因为b和a不是同一个强连通分量内的点,所以b到a一定没有通路,那么a不被b欢迎,于是a所在的连通分量一定不是解集的那个连通分量。
3:如果存在两个独立的强连通分量a和b,那么a内的点和b内的点一定不能互相到达,那么,无论是a还是b都不是解集的那个连通分量,问题保证无解。
4:如果图非连通,那么,至少存在两个独立的连通分量,问题一定无解。
这是看大牛的解析,下面是我写的代码:
#include<iostream>//求强联通分量和割点的不一样的#include<cstdio>#include<string>#include<cstdio>#include<cmath>#include<algorithm>#include<queue>#include<stack>#include<vector>#include<climits>using namespace std;#define max(a,b) (a)>(b)?(a):(b)#define min(a,b) (a)<(b)?(a):(b) #define rep(i,n) for(i=0; i<(n); i++)#define reph(i,n,m) for(i=(n); i<=(m); i++)//正循环的#define repd(i,n,m) for(i=(n); i>=(m); i--) //负循环的#define max(a,b) (a)>(b)?(a):(b)#define min(a,b) (a)<(b)?(a):(b)#define fab(a) (a)>0?(a):0-(a)#define arc(a) (a)*(a)#define inf 10000000 //最大值的#define exp 0.0000001 //浮点型的#define N 11000 //记录开的数组bool vis[N],mark[N];int flag[N];int Stack[N];typedef struct fun{int y,pre;}rr;fun a[N*5];int n,m;int dfn[N],low[N],pre[N];int num[N];int len,sum;void add(int s,int t){ a[len].y=t;a[len].pre=pre[s];pre[s]=len++;}void dfs(int x){Stack[m++]=x;dfn[x]=low[x]=len++;//进行操作的mark[x]=true;//代表入栈了int i;for(i=pre[x]; i!=0; i=a[i].pre){int y=a[i].y;if(!dfn[y])//没有在栈中{dfs(y);low[x]=min(low[x],low[y]); }else if(mark[y]==true)//代表进过栈并且没有出栈的low[x]=min(low[x],low[y]);}if(dfn[x]==low[x])//即x为强联通分量的分割点{ ++sum; do{//其实是包括v点的 i=Stack[--m]; mark[i]=false;//出栈了 flag[i]=sum;//代表的是等级的 }while(i!=x);//即为出栈的情况的}}int main(){while(scanf("%d%d",&n,&m)!=EOF){len=1;int s,t;memset(dfn,0,sizeof(dfn));memset(flag,0,sizeof(flag));memset(pre,0,sizeof(pre)); memset(mark,0,sizeof(mark));while(m--){scanf("%d%d",&s,&t);add(s,t);//进行加加} len=1;m=1;int i;sum=0; reph(i,1,n) { if(!dfn[i])//对于i是没有入栈的 dfs(i);// } memset(mark,false,sizeof(mark));int k;reph(i,1,n){for(k=pre[i]; k!=0; k=a[k].pre)if(flag[i]!=flag[a[k].y])//两个不在一个等级上,即i点有出度,说明肯定部位最后的mark[flag[i]]=true;}int p,cnt=0;reph(i,1,sum){if(!mark[i])//代表为假,在这个等级上的点是一个圈{++cnt;//强连通分量的个数p=i;}}if(cnt>1)//出度为0的强联通分量的个数比较多,则就没有最高的点了printf("0\n");else//p记录的是强连通分量的标号{ cnt=0;reph(i,1,n){if(flag[i]==p)++cnt;}printf("%d\n",cnt);}} return 0;}
poj3352
题目大意:给n个景点和n个景点之间的路径(路径之间无交叉),让求可以填加的最小的路径数使得任意两个景点之间最少有两条路!
解题思路:可以借用上道题的思路,但此题是无向图,注意不能对一条边重复来回操作!
找出图中所有的强连通分量可以看作是一个点,因为题中说明原来的景点都是相连的,所有这些强连通分量之间肯定是相连的,求出每个连通分量的入度,则需要加入的边就为所有度为1的连通分量的个数加1再除以2,然后代码几乎和上面的是差不多的。
#include<iostream>#include<cstdio>#include<string>#include<cstdio>#include<cmath>#include<algorithm>#include<queue>#include<stack>#include<vector>#include<climits>using namespace std;#define max(a,b) (a)>(b)?(a):(b)#define min(a,b) (a)<(b)?(a):(b) #define rep(i,n) for(i=0; i<(n); i++)#define reph(i,n,m) for(i=(n); i<=(m); i++)//正循环的#define repd(i,n,m) for(i=(n); i>=(m); i--) //负循环的#define max(a,b) (a)>(b)?(a):(b)#define min(a,b) (a)<(b)?(a):(b)#define fab(a) (a)>0?(a):0-(a)#define arc(a) (a)*(a)#define inf 10000000 //最大值的#define exp 0.0000001 //浮点型的#define N 1010 //记录开的数组bool mark[N];int dfn[N],low[N];int n,m,sum,len;typedef struct fun{int y,pre;}rr;fun a[N*2];stack<int>q;int pre[N];int flag[N];void add(int s,int t){a[len].y=t;a[len].pre=pre[s];pre[s]=len++;}void dfs(int x,int fa){ q.push(x); dfn[x]=low[x]=len++; mark[x]=true; int i,y; for(i=pre[x]; i!=0; i=a[i].pre) { y=a[i].y; if(y==fa)//不能用两个边的 continue; if(!dfn[y])//y没有在栈里面 { dfs(y,x); low[x]=min(low[x],low[y]); } else if(mark[x]) low[x]=min(low[x],low[y]); } if(low[x]==dfn[x]) { ++sum; while(true) { i=q.top(); q.pop(); flag[i]=sum; mark[i]=false;//代表出栈并且已经找到自己最大的连通分量了 if(i==x) break; } }}int main(){while(scanf("%d%d",&n,&m)!=EOF){memset(mark,false,sizeof(mark));//memset(vis,false,sizeof(vis));memset(pre,0,sizeof(pre));memset(dfn,0,sizeof(dfn));memset(flag,0,sizeof(flag));int i,j,s,t;len=1;while(m--){scanf("%d%d",&s,&t);add(s,t);add(t,s);} len=1;sum=0;reph(i,1,n) if(!dfn[i])//没有入栈 dfs(i,-1); int jilu[N];memset(jilu,0,sizeof(jilu));reph(i,1,n){for(j=pre[i];j !=0; j=a[j].pre){if(flag[i]!=flag[a[j].y])//不在同一个联通分量里面的jilu[flag[a[j].y]]++;}}int rr=0;reph(i,1,sum)if(jilu[i]==1)rr++;printf("%d\n",(rr+1)/2);} return 0;}
- 双连通分量 poj3352 poj2186
- poj3352[无向图双连通分量]
- POJ3352 Road Construction (双连通分量)
- poj3352 Road Construction(边双连通分量)
- POJ3352 Road Construction 双连通分量和桥 tarjan
- POJ3352.Road Construction——边-双连通分量
- poj3352——Road Construction(双连通分量)
- poj3352 Road Construction 边双连通分量tarjan算法
- POJ3352 桥,边双连通分量,构造边双连通图
- 【POJ3352】Road Construction tarjan求边-双连通分量,裸题模板题
- 双连通分量
- 双连通分量_road
- 边双连通分量
- 双连通分量
- 双连通分量-tarjan
- 双连通分量
- 双连通分量
- 双连通分量-tarjan
- 简单python调试命令
- Chrome 技巧:重新打开被禁止翻译的站点或语言的翻译功能
- 联合体初始化的方法
- 同步异步和阻塞非阻塞
- 流处理框架Storm简介
- 双连通分量 poj3352 poj2186
- NSIS 安装完毕后如何刷新文件关联
- Linux Socket编程(不限Linux)
- Eclipse中如何更改字体及字体大小 Eclipse 更改字体方法
- 中科大洋
- 一个Java程序从生到死的旅程
- 安装linux的悲惨经历
- 深拷贝与浅拷贝
- 矩阵快速幂