【个人专题一】强连通——Poj_2186

来源:互联网 发布:vue.js 2.0 api文档 编辑:程序博客网 时间:2024/04/30 02:55

思路1代码:#include<stdio.h>#include<vector>//#include<memory.h>using namespace std;//存储变量int x,y;vector<int> f[10000];//存储正向图vector<int> n[10000];//存储反向图bool sign[10000];//辅助变量int i,g,h,sum,r;void init(){//初始化memset(sign,0,sizeof(sign));sum=0;//清空相连表for(i=0;i<x;i++){f[i].clear();n[i].clear();}    //存储相连表for(i=0;i<y;i++){scanf("%d%d",&g,&h);f[g-1].push_back(h-1);n[h-1].push_back(g-1);}r=0;}void search(int k,vector<int> l[10000]){vector<int>::iterator t;sign[k]=true;sum++;for(t=l[k].begin();t!=l[k].end();t++){if(!sign[*t])search(*t,l);}}int doit(){//在反向图里搜索源_强连通子图(即正向图的出度为0的强连通子图)for(i=0;i<x;i++){if(!sign[i]){r=i;search(r,n);}}//如果以r作为起点在反向图中深搜能遍历全图,则r就是受全部人欢迎的牛,否则不存在受全部人欢迎的牛,返回0sum=0;memset(sign,0,sizeof(sign));search(r,n);if(sum<x) return 0;//如果存在r,则与r在同一个强连通子图的牛都是受全部人欢迎的牛,计算其数目(因为已知r在反向图中能找到所有,//即正向图中都能找到r,则正向图中以r为根节点进行深搜,能碰到的点都是和r在同一个强连通子图中sum=0;memset(sign,0,sizeof(sign));search(r,f);return sum;}int main(){while(scanf("%d%d",&x,&y)!=EOF){init();printf("%d\n",doit());}return 0;}思路2代码:#include<iostream>using namespace std;#define DOTMAX 10001#define EDGEMAX 50001struct node{    int t;    node *next;}dotset[EDGEMAX*3];//直接申请好全部空间,然后哪个空间放哪个节点临时生成即可int count=0;//每一个case后,count置为0node *Newnode()//调用上面申请好的全部空间里的一个节点作为存储地点{    node *p;    p=&dotset[count];    count++;    return p;}void Addnode(node hash[],int a,int b)//在相连表上插入一个节点{    node *p=&hash[a];    node *q=Newnode();    q->t=b;    q->next=p->next;    p->next=q;}node hash[DOTMAX];node nhash[DOTMAX];node New[DOTMAX];int gcc;int order[DOTMAX];int num;int id[DOTMAX];int visit[DOTMAX];int gccnum[DOTMAX];void init(node hash[],int n)//初始化相连表{    count=0;    int i;    for(i=1;i<=n;i++)    {        hash[i].t=-1;        hash[i].next=NULL;    }}int n,m;void dfs(int u)//正向图中深搜{    visit[u]=1;    node *p;    int v;    for(p=hash[u].next;p!=NULL;p=p->next)    {        v=p->t;        if(!visit[v])        {            dfs(v);        }    }    num++;    order[num]=u;//用于反向搜索的顺序,可避免排序从而提高效率}void ndfs(int u)//反向图中搜索{    visit[u]=1;    id[u]=gcc;    node *p;    int v;    for(p=nhash[u].next;p!=NULL;p=p->next)    {        v=p->t;        if(!visit[v])        {            ndfs(v);        }    }}int main(){    int a,b,i;    while(scanf("%d%d",&n,&m)!=EOF)    {//初始化三个相连表        init(hash,n);        init(nhash,n);        init(New,n);        for(i=1;i<=m;i++)        {            scanf("%d%d",&a,&b);            Addnode(hash,a,b);            Addnode(nhash,b,a);        }        memset(visit,0,sizeof(visit));        num=0;//正向深搜,找到根节点        for(i=1;i<=n;i++)        {            if(!visit[i])                dfs(i);        }        memset(visit,0,sizeof(visit));        gcc=0;//反向深搜        for(i=num;i>=1;i--)        {            if(!visit[order[i]])            {                gcc++;                ndfs(order[i]);            }        }        for(i=1;i<=n;i++)        {            node *p;            for(p=hash[i].next;p!=NULL;p=p->next)            {                if(id[i]!=id[p->t])//如果不是属于同一个强连通                {                    Addnode(New,id[i],id[p->t]);//构建新图相连表                }            }        }        int cnt=0;        memset(gccnum,0,sizeof(gccnum));        for(i=1;i<=n;i++)            gccnum[id[i]]++;//各连通子图中的顶点个数        int mark=0;        for(i=1;i<=gcc;i++)//搜索gcc个子图(视为顶点)        {            if(New[i].next==NULL)//出度为0            {                cnt++;                mark=i;            }        }        if(cnt==1)//如果出度为0的只有一个,该连通子图的个数即所求个数            printf("%d\n",gccnum[mark]);        else//无结果            printf("%d\n",0);    }return 0;} 自己照思路2敲了一遍。。(344Ms)#include<stdio.h>#include<string.h>struct Node{int sub;Node *next;}hash[10001],nhash[10001],newhash[10001];bool IsVisit[10001];int N,M,order[10001],sum,setNum[10001],num[10001],setnum;//sum代表已经访问结束的顶点个数void init(int n)//初始化{int i;for(i=1;i<=n;i++)hash[i].next=nhash[i].next=newhash[i].next=NULL;memset(IsVisit,false,sizeof(IsVisit));memset(num,0,sizeof(num));sum=0;setnum=0;//强连通子图的标记}void AddNode(struct Node thash[],int tstt,int tend)//添加节点{struct Node *p=new struct Node;p->sub=tend;p->next=thash[tstt].next;thash[tstt].next=p;}void dfs(int x){IsVisit[x]=true;struct Node *p;for(p=hash[x].next;p!=NULL;p=p->next){if(!IsVisit[p->sub])dfs(p->sub);}order[++sum]=x;}void ndfs(int x){IsVisit[x]=true;setNum[x]=setnum;num[setnum]++;Node *p;for(p=nhash[x].next;p!=NULL;p=p->next){if(!IsVisit[p->sub])ndfs(p->sub);}}int main(){while(scanf("%d%d",&N,&M)!=EOF){init(N);int i,stt,end;for(i=1;i<=M;i++){scanf("%d%d",&stt,&end);AddNode(hash,stt,end);AddNode(nhash,end,stt);}for(i=1;i<=N;i++){if(!IsVisit[i]){dfs(i);}}memset(IsVisit,false,sizeof(IsVisit));for(i=N;i>0;i--){if(!IsVisit[order[i]]){setnum++;ndfs(order[i]);}}for(i=1;i<=N;i++){Node *p;for(p=hash[i].next;p!=NULL;p=p->next)//将强连通子图缩为一点,构建新图{if(setNum[i]!=setNum[p->sub])AddNode(newhash,setNum[i],setNum[p->sub]);}}int hole=0,k;for(i=1;i<=setnum;i++){if(newhash[i].next==NULL){hole++;k=i;}}if(hole==1) printf("%d\n",num[k]);else printf("0\n");}return 0;}

 题目大意:在一群牛中,寻找受所有牛欢迎的牛的数量

思路1:

1、在反向图中深搜,找到“根节点”(最后结束的是“根节点”),然后在反向图中深搜“根节点”,如果能遍历全部顶点,则该点并是反向图中的根节点,也就是受所有牛欢迎的牛,转到2。否则输出0,结束。

2、 和该根节点的牛在同一强连通子图的牛也是受全部牛欢迎的牛。这里只要在正向图中深搜根节点,记录搜索到的数目,就是我们要的输出。

 

思路2:

1、在正向图中深搜,形成森林,记录各个节点的结束时间。

2、按时间从大到小在反向图中深搜,深搜未中断前,给每个被搜顶点记录相同且唯一的数字,代表属于同一个强连通子图(以上为Kosaraju算法)

3、利用强连通子图构建新图(此时必定无环,有环的已经属于同一个强连通子图而缩为新图的一个顶点

4、如果新图中出度为0的节点有多个,则返回0。否则返回出度为0的那个强连通子图中顶点的个数。

(思路2的代码来自http://www.cppblog.com/abilitytao/archive/2009/09/26/97302.html,不过我加了注释,需要吸收的是相连表用vector建立十分耗时,此外思路2的找强连通子图的做法在深搜完后不用排序即可立即进行反向搜索(可能已经很通用,只是我刚学,自己的思路是要排序,呵呵)