• POJ2186Popular Cows


     转载请注明出处:優YoU http://blog.csdn.net/lyy289065406/article/details/6764104

    大致题意:

    有N只奶牛,其中奶牛A认为奶牛B备受注目,而奶牛B也可能认为奶牛C备受注目。奶牛们的这种“认为”是单向可传递的,就是说若奶牛A认为奶牛B备受注目,但奶牛B不一定会认为奶牛A备受注目。而当A认为B备受注目,且B认为C备受注目时,A一定也认为C备受注目。

           现在给出M对这样的“认为...备受注目”的关系对,问有多少只奶牛被除其本身以外的所有奶牛关注。

    解题思路:

    极大强连通分量+缩点。

    发现自从用Tarjan算法做了POJ2942之后,这些利用Tarjan算法的题目都是水题。

    构造模型:

    N个顶点的有向图G,有M条边。求一共有多少个点,满足这样的条件:所有其它的点都可以到达这个点。

    首先,这个题的N和M都非常大,暴搜肯定TLE。

    考虑一下,如果图G是一棵有向树,那么问题就变的很简单了,因为当且仅当这棵树只有一个叶子结点(出度为0的点)时,树上的其他所有结点都能到达这个点。而当有向树上有1个以上的叶子时,都是无解的。

    由于树是无环的,下面成这样的一棵有向树为 有向无环树DAG

    那么我们能否把图转化为树去求解呢?

    首先可以想到的是,如果图G中包含有环,那么就可以把这个环缩成一个点,因为环中的任意两个点可以到达,环中所有的点具有相同的性质,即它们分别能到达的点集都是相同的,能够到达它们的点集也是相同的。

    那么是否只有环中的点才具有相同的性质呢?进一步的考虑,图中的每一个极大强连通分量中的点都具有相同的性质。所以,如果把图中的所有极大强连通分量求出后,对每个极大强连通分量缩点,就可以把图收缩成一棵有向无环树DAG,那么只要判断出度为0的缩点是否只有1个,若DAG中有且仅有1个这样的缩点,则输出缩点(图G的极大强连通分量)内所包含的图G的结点个数,问题就解决。

    预备知识:Tarjan算法求有向图的极大强连通分量。

    相关知识点传送门:

    有关图论的知识点的定义:http://www.byvoid.com/blog/biconnect/

    Tarjan算法入门基础:

    http://hi.baidu.com/lydrainbowcat/blog/item/42a6862489c98820c89559f3.html

    补充一个小内容:

    用Tarjan算法求极大强连通分量时,对于有向边s->t

    1、  若DFN[t]==0,则s->t是一条树边,t尚未入栈;

    2、  若DFN[t]<DFN[s],当t在栈中,s->t为一条后向边;当t已经出栈,s->t为一条横叉边。

    注意只有有向图有横叉边,无向图不存在横叉边的概念。

    对横叉边的处理:无视掉。

    Source修正

    USACO 2003 Fall

    http://www.4ucode.com/Study/Topic/1377469

    //Memory Time 
    //2116K  266MS 
    
    #include<iostream>
    #include<stack>
    using namespace std;
    
    /*图G的结点的存储结构*/
    class Node
    {
    public:
    	int id;
    	class Node* next;
    	Node():id(0),next(0){}
    };
    
    /*缩点(极大强连通分量)的存储结构*/
    class Shrink_point
    {
    public:
    	int in;		//缩点入度
    	int out;	//缩点出度
    	int num;	//缩点内含有图G的结点的个数
    	Shrink_point():in(0),out(0),num(0){}
    };
    
    /*******************************************************/
    
    class solve
    {
    public:
    	solve(int n,int m):N(n),M(m)
    	{
    		Initial();
    		Input_Creat();
    
    		/*注意:有向图G不一定从任何位置开始搜索都能遍历所有点*/
    		for(int i=1;i<=N;i++)
    			if(DFN[i]==0)
    			{
    				stack_Node.push(i);		//搜索起点入栈
    				Status[i]=1;
    				Tarjan(i);
    			}
    
    		printf("%d\n",solution());
    
    	}
    	~solve()
    	{
    		delete[] DFN;
    		delete[] Low;
    		delete[] Status;
    		delete[] SCC;
    		delete[] sp;
    
    		EmptyList();
    
    		while(!stack_Node.empty())
    			stack_Node.pop();
    	}
    
    	int min(int a,int b) const{return a<b?a:b;}
    
    	void Initial(void);		//申请存储空间并初始化
    	void Input_Creat(void);	//输入并构造有向图G
    	void Tarjan(int s);		//寻找图G的所有极大强连通分量
    	int solution(void);		//若缩点图只有1个出度为0的缩点,返回缩点内包含的结点数。否则无解,返回0
    
    	void DelLink(Node* p);	//释放以p为表头的整条链
    	void EmptyList(void);	//释放邻接链表
    
    protected:
    	int N;					//the number of cows
    	int M;					//the number of popular pairs
    	Node** LinkHead;		//邻接链表表头
    
    	int TimeStamp;			//(外部)时间戳
    	int* DFN;				//DFN[u]: 结点u的搜索次序(时间戳)
    	int* Low;				//Low[u]: 结点u或u的子树能够追溯到的最早的栈中结点的次序号
    
    	stack<int>stack_Node;	//辅助栈,用于寻找极大强连通分量
    	int* Status;			//Status[i]-> 0:i未入栈  1:i在栈中  2:i已出栈
    	int* SCC;				
    	int SCC_id;				//SCC[i]=SCC_id  图G中结点i所属的极大强连通分量(缩点)的编号为SCC_id
    	Shrink_point* sp;		//存储每个缩点(极大强连通分量)的信息
    };
    
    void solve::Initial(void)
    {
    	LinkHead=new Node*[N+1];
    	for(int i=1;i<=N;i++)
    		LinkHead[i]=0;
    
    	TimeStamp=0;
    	DFN=new int[N+1];
    	Low=new int[N+1];
    	memset(DFN,0,sizeof(int)*(N+1));
    	memset(Low,0,sizeof(int)*(N+1));
    
    	SCC_id=0;
    	SCC=new int[N+1];
    	Status=new int[N+1];
    	memset(Status,0,sizeof(int)*(N+1));
    
    	sp=new Shrink_point[N+1];
    
    	return;
    }
    
    void solve::Input_Creat(void)
    {
    	int a,b;
    	for(int j=1;j<=M;j++)
    	{
    		scanf("%d %d",&a,&b);
    
    		if(!LinkHead[a])
    			LinkHead[a]=new Node;
    
    		Node* tmp=LinkHead[a]->next;
    		LinkHead[a]->next=new Node;
    		LinkHead[a]->next->id=b;
    		LinkHead[a]->next->next=tmp;
    	}
    	return;
    }
    
    void solve::Tarjan(int s)
    {
    	DFN[s]=Low[s]=++TimeStamp;
    	if(LinkHead[s])
    	{
    		for(Node* p=LinkHead[s]->next;p;p=p->next)
    		{
    			int t=p->id;
    			if(DFN[t]<DFN[s])
    			{
    				if(DFN[t]==0)			//s->t为树枝边
    				{
    					stack_Node.push(t);
    					Status[t]=1;
    
    					Tarjan(t);
    					Low[s]=min(Low[s],Low[t]);
    				}
    				else if(DFN[t]!=0 && Status[t]==1)	//s->t为后向边
    				{
    					Low[s]=min(Low[s],DFN[t]);
    				}
    			}
    		}
    	}
    	if(DFN[s]==Low[s])	//找到极大强连通分量
    	{
    		SCC_id++;
    		for(int node=stack_Node.top();;node=stack_Node.top())
    		{
    			stack_Node.pop();
    			Status[node]=2;
    			SCC[node]=SCC_id;
    			sp[ SCC_id ].num++;
    
    			if(node==s || stack_Node.empty())
    				break;
    		}
    	}
    	return;
    }
    
    int solve::solution(void)
    {
    	/*计算所有缩点的入度和出度*/
    
    	for(int i=1;i<=N;i++)
    		if(LinkHead[i])
    		{
    			for(Node* p=LinkHead[i]->next;p;p=p->next)
    			{
    				int j=p->id;
    				if(SCC[i]!=SCC[j])
    				{
    					sp[ SCC[i] ].out++;
    					sp[ SCC[j] ].in++;
    				}
    			}
    		}
    
    	/*寻找出度为0的缩点*/
    
    	int cnt=0;		//记录出度为0的缩点个数
    	int pk;			//记录出度为0的缩点编号
    
    	for(int k=1;k<=SCC_id;k++)
    		if(sp[k].out==0)
    		{
    			cnt++;
    			pk=k;
    		}
    	if(cnt!=1)			//出度为0的缩点的个数不为1,本题无解
    		return 0;
    	
    	return sp[pk].num;	//返回出度为0的缩点所包含图G中的结点个数
    }
    
    void solve::DelLink(Node* p)
    {
    	if(p->next)
    		p=p->next;
    	delete[] p;
    	return;
    }
    
    void solve::EmptyList(void)
    {
    	for(int i=1;i<=N;i++)
    		if(LinkHead[i])
    			DelLink(LinkHead[i]);
    	return;
    }
    
    int main(void)
    {
    	int n,m;
    	while(scanf("%d %d",&n,&m)!=EOF)
    		solve poj2186(n,m);
    
    	return 0;
    }
    
    [ EXP技术分享博客 ] 版权所有,转载请注明出处: http://exp-blog.com
  • 相关阅读:
    【转】PHP使用共享内存进程间通信
    【转】php进程间通信--有名管道
    【转】代理模式-php白话示例
    [转]设计的核心任务之三:确保正交性
    pc主板支持独显和集显视频输出
    OpenGL核心技术之抗锯齿
    读书:有钱人想的和你不一样
    js 文本相似度
    js实现获得QQ截图或者微信截图后剪切板的内容clipboardData
    【转】chrome浏览器F12 Network中Timing参数含义
  • 原文地址:https://www.cnblogs.com/lyy289065406/p/2172643.html
Copyright © 2020-2023  润新知