Popular Cows
Time Limit: 2000MS |
|
Memory Limit: 65536K |
Total Submissions: 11739 |
|
Accepted: 4641 |
Description
Every cow's dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.
Input
* Line 1: Two space-separated integers, N and M
* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.
Output
* Line 1: A single integer that is the number of cows who are considered popular by every other cow.
Sample Input
3 3
1 2
2 1
2 3
Sample Output
1
Hint
Cow 3 is the only cow of high popularity.
这道题刚开始我用的普通DFS直接搜索,不幸的是超内存了,因为我无知的开了一个长度为1亿的字符数组(PS:以后开大数组前一定要先算一下需要的内存),被MLE后不甘心继续修改,把内存降下来后继续提交,TLE,再改,再TLE。。若干次后我意识到普通算法好像过不去。。也没有心思再改了。。。
看了讨论才知道这个需要用求强连通分量的tarjan算法,百度上找到tarjan,看了挺长时间才把他真正看懂。。
看懂了tarjan,我又想了一个多小时,写出代码,终于将这道题AC。。真不容易,不过这道题让我学到了新的东西,A掉这题的成就感很强,在此我要感谢这道题~,还要感谢帮助我的小杰同学~!
思路:用tarjan算法求出图的强连通分量数C,将每个强联通分量看成一个点(缩点),然后求出每个缩点的出度,判断这些点的出度是否等于C-1,如果相等,则出度为0的那个连通分支所包含的点数即是所求,否则输出0。
证明:假设已经求出关系图G的各个连通分量,由连通分支内任意两点都可达的性质可以得到:该分支内的任意一点都能到达这个点。将每个连通分量缩成一个点,对于一个点来说,只要其他点存在到这个点的单向路径则这个缩点内包含的点都被其他的连通分支所包含的点支持,因此,题目也就是求关系图每个连通分支的出度,如果只存在一个出度为0的连通分支则这个联通分支内包含的点数即是所求,否则不存在被所有牛支持的牛。
#include<iostream> using namespace std; int dfn[10010],low[10010],time,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]=++time; S[top++]=v; 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; /*缩图,标记属于同一强连通分量的点*/ }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; } time=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; }