双连通分量 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;}


 

原创粉丝点击