思路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就是受全部人欢迎的牛,否则不存在受全部人欢迎的牛,返回0
sum=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 50001
struct node
{
int t;
node *next;
}dotset[EDGEMAX*3];//直接申请好全部空间,然后哪个空间放哪个节点临时生成即可
int count=0;//每一个case后,count置为0
node *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的找强连通子图的做法在深搜完后不用排序即可立即进行反向搜索(可能已经很通用,只是我刚学,自己的思路是要排序,呵呵)