void-man

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
有N(N<=10000)头牛,每头牛都想成为most poluler的牛,给出M(M<=50000)个关系,如(1,2)代表1欢迎2,关系可以传递,但是不可以相互,即1欢迎2不代表2欢迎1,但是如果2也欢迎3那么1也欢迎3.
给出N,M和M个欢迎关系,求被所有牛都欢迎的牛的数量。
用强联通分量做,求连通分量我用的是Kosaraju算法。
首先求出联通分量的个数,然后依次求各个联通分量的出度,如果仅有一个连通分量出度为0则这个联通分量内的点的个数就是答案;如果有多于一个的联通分量的出度为0,则说明此有向图肯定不连通。因此直接输出0。
可以用Tarjan算法或者kosaraju算法来求解,但是俩算法太难理解透彻了,直接扔上代码回头整理个模版出来吧
Tarjan
#include<iostream>
using namespace std;
int dfn[10010],low[10010],times,C,s[10010],S[10010],top;		bool in[10010];
struct LIST
{
	int v;	LIST *next;
};

LIST *head[10010],*rear[10010];

int chudu[10010];

void tarjan(int v)		/*tarjan求图的强连通分量*/
{
	dfn[v]=low[v]=++times;//dfn存储时间戳,就是被dfs搜到的次序 ,
	S[top++]=v;      //Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号 
	in[v]=true;
	for(LIST *p=head[v];p!=NULL;p=p->next)
		if(!dfn[p->v])
		{
			tarjan(p->v);
			if(low[p->v]<low[v])
				low[v]=low[p->v];
		}
		else if(in[p->v]&&low[p->v]<low[v])
			low[v]=low[p->v];

	if(low[v]==dfn[v])
	{
		C++;
		do
		{
			v=S[--top];
			in[v]=false;
			s[v]=C;		/*缩图,标记属于同一强连通分量的点,点v属于第C个连通分图*/
		}while(low[v]!=dfn[v]);
	}
}


int main()
{
	int n,m,i,a,b;
	while(cin>>n>>m)
	{
		memset(head,0,sizeof(int)*10010);
		memset(rear,0,sizeof(int)*10010);
		memset(s,0,sizeof(int)*10010);
		top=0;
		for(i=0;i<m;i++)			/*邻接表存储关系图*/
		{
			scanf("%d%d",&a,&b);
			if(rear[a]!=NULL)
			{
				rear[a]->next=new LIST;
				rear[a]=rear[a]->next;
			}
			else
				head[a]=rear[a]=new LIST;
			rear[a]->next=NULL;
			rear[a]->v=b;
		}
		times=0;	C=0;
		memset(dfn,0,sizeof(int)*10010);
		memset(low,0,sizeof(int)*10010);
		memset(in,0,sizeof(bool));
		for(i=1;i<=n;i++)
			if(!dfn[i])
				tarjan(i);
		if(C==1)				/*如果只有一个强连通分量,则输出n*/
		{
			cout<<n<<endl;
			continue;
		}

		memset(chudu,0,sizeof(chudu));

		for(i=1;i<=n;i++)						/*计算缩图后每个点的出度*/
			for(LIST *p=head[i];p!=NULL;p=p->next)
				if(s[i]!=s[p->v])
					chudu[s[i]]=1;
		a=b=0;
		for(i=1;i<=C;i++)
			if(chudu[i])
				a++;
			else
				b=i;
		if(a==C-1)			/*统计出度总数是否为C-1*/
		{
			a=0;			/*如果出度总数为C-1,则统计出度为0的连通分量包含点的个数*/
			for(i=1;i<=n;i++)
				if(s[i]==b)
					a++;
			cout<<a<<endl;
		}
		else				/*如果出度总数不为C-1,则无解,输出0*/
			cout<<'0'<<endl;

	}
	return 0;

}
kosaraju
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int MAX = 10005;//v为原图 ,rv为所有边逆向后的新图 
vector<int> v[MAX], rv[MAX], s;//s记录第一遍dfs后各点的结束时间:最晚结束的在栈顶 
int num[MAX], tree_n[MAX], cnt;// num[v]点v所属连通分量的编号 * tree_n[i]第i个连通分量内点的个数 
bool mark[MAX];
void dfs_1(int x)
{    
    mark[x] = 1;        
    for (int i = 0; i < v[x].size(); ++i)          
        if (!mark[v[x][i]])                
            dfs_1(v[x][i]);                
    s.push_back(x);
}
void dfs_2(int x)
{    
    num[x] = cnt;     //点v所属连通分量的编号为cnt        
    ++tree_n[cnt]; mark[x] = 1;    
    for (int i = 0; i < rv[x].size(); ++i)           
        if (!mark[rv[x][i]])                   
            dfs_2(rv[x][i]);        
}
    void cal(int n)
    {    
        memset(mark, 0, sizeof(mark));    
        for (int i = 1; i <= n; ++i)    
        {        
            for (int j = 0; j < v[i].size(); ++j)        
            {            
                int x = v[i][j];            
                if (num[i] != num[x]) //如果ij相连的边不再同一个连通分量                          
                    mark[num[i]] = 1;  //这个点i标记,出度不是0 
            }    
        }        
        int flag = 0, ans;    
        for (int i = 1; i <= cnt; ++i)    
        {        
        if (!mark[i])        
        {            
        ans = i;            
        ++flag;        
        }    
        }    
        if (flag == 1)printf("%d\n", tree_n[ans]);    
        else printf("0\n");
}        
int main()
{int a,b,n,m,i,j;
      scanf("%d%d",&n,&m);       
     for (i = 0; i < m; ++i)    
      {    scanf("%d%d", &a, &b);        
          v[a].push_back(b);        
         rv[b].push_back(a);//反图存储 
      }      
 for (i = 1; i <= n; ++i)    
   if (!mark[i])        
    dfs_1(i);      
     memset(mark, 0, sizeof(mark));    
     memset(tree_n, 0, sizeof(tree_n));    
     cnt = 0;        //cnt记录连通分量的个数    
     for (i = s.size() - 1; i >= 0; --i)    
     {        
         if (!mark[s[i]])        
         {            
             ++cnt;            
             dfs_2(s[i]);        
         }    
     }    
     cal(n);    
     return 0;
}

posted on 2011-05-16 22:48  void-man  阅读(1987)  评论(0编辑  收藏  举报