POJ 2186:Popular Cows(tarjan算法入門題)


題目鏈接:http://poj.org/problem?id=2186

題目意思:每一個牛的願望是稱為牛圈中最受歡迎的牛,在一個牛圈中共有N頭牛,給出M對關系(A,B),

意思是牛A認為牛B很受歡迎,而且這種想法是會傳遞的,如果A認為B很受歡迎,B認為C很受歡迎,那么

A就會認為C是受歡迎的。你的任務是計算有多少牛讓其他所有牛都認為它是最受歡迎的。

題目是一個強連通算法的入門題目。根據題目描述,我們就可以知道應該給出的一個關系網是有環存在的,

不然也不必問多少只牛讓其他所有牛認為它是受歡迎的。

比如下面一組數據:自己隨便寫的一個數據

8 9

1 2

2 3

3 4

4 5

5 3

5 6

6 7

7 8

8 6


畫出來就是這個圖,然后可以看出對於6,7,8這三個牛來說,除自身外的所有牛都覺得自己

受歡迎,因此最后答案就是3.


解題思路:首先是進行縮點,給各個連通分量給出一個編號,我們把一個連通分量中的點同意看

成一個點,並用這個編號表示,對此對於縮點后的圖來說,必須只能有一個出度為0的點,如果

多余一個的話,不能存在一些牛使其他所有牛都認為自己受歡迎。則答案就是0,否則我們就把

出度為0的點所在的連通分量的點做一個統計,統計到的點的個數就是答案。


POJ這個題目數據弱了,好多人說算法寫得不對也可AC,不過我覺得自己寫的是沒問題的,測試了

討論區的數據,差不多也都過了,感覺自己寫的應該是正確的算法。


#include <iostream>
#include <stdio.h>
#include <string.h>

using namespace std;

const int maxm = 600000;
const int maxn = 200000;
int head[maxn]; ///鏈表頭節點
int Stack[maxn]; ///棧
bool inStack[maxn]; ///用來標記節點是否在棧中
int belong[maxn]; ///各個頂點所屬於的連通分量
int dfn[maxn]; ///時間標記
int low[maxn]; ///相當於並查集
int outdegree[maxn]; ///統計頂點入度
int edgeNumber,time,top,cnt; ///time時間標記,top棧頂指針,cnt當前連通分量編號
struct Edge
{
int A;
int B;
int nex;
}edge[maxm];
void addEdge(int a,int b)
{
edge[edgeNumber].A = a;
edge[edgeNumber].B = b;
edge[edgeNumber].nex = head[a];
head[a] = edgeNumber++;
}
///強連通找各個連通分量所屬的集合
void tarjan(int a)
{
dfn[a] = low[a] = ++time;
Stack[++top] = a;
inStack[a] = true;
for(int i = head[a]; i != -1; i = edge[i].nex)
{
int b = edge[i].B;
if(!dfn[b]) ///沒有被訪問過
{
tarjan(b);
low[a] = min(low[a],low[b]);
}
else if(inStack[b])
{
low[a] = min(low[a],dfn[b]);
}
}
if(dfn[a] == low[a])
{
cnt++;
int temp;
do
{
temp = Stack[top--];
inStack[temp] = false;
belong[temp] = cnt; ///當前頂點屬於第cnt個連通分量
}while(temp != a);
}
}
void solve(int n,int m)
{
int a,b;
edgeNumber = 0;
memset(head,-1,sizeof(head));
for(int i = 1; i <= m; i++)
{
scanf("%d%d",&a,&b);
addEdge(a,b);
}
top = time = cnt = 0;
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(outdegree,0,sizeof(outdegree));
for(int i = 1; i <= n; i++)
inStack[i] = false;
for(int i = 1; i <= n; i++)
{
if(dfn[i]==0)
tarjan(i);
}
for(int i = 0; i < edgeNumber; i++)
{
a = edge[i].A;
b = edge[i].B;
a = belong[a];
b = belong[b];
if(a != b) ///不屬於一個連通分量的話,我們統計出度
outdegree[a]++;
}
int num = 0;
int temp;
for(int i = 1; i <= cnt; i++)
{
if(outdegree[i]==0)
{
temp = i;
num++;
}
}
int ans = 0;
if(num == 1) ///出度為1的點必須有且只能有一個
{
for(int i = 1; i <= n; i++)
{
if(belong[i] == temp)
ans++;
}
}
printf("%d\n",ans);
}
int main()
{
int N,M;
while(~scanf("%d%d",&N,&M)) ///N頭牛,M個關系
{
solve(N,M);
}
return 0;
}




注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com