考察算法:强连通分量+压缩点。
先说明下题目的大意。
有一群牛在相互崇拜。崇拜是单向的。且崇拜是可传递的,即a->b,b->c,可以得到a->c。
分别告诉你总共有n头牛,m条彼此间的崇拜关系,让你求出有多少头牛是其他牛都崇拜的,这里称之为明星牛~~。
思路:
要求出明星牛的个数,可以很轻松的想到作为明星牛,是不能崇拜其他牛的~,但是问题来了。
然后明星牛的个数一定是1嘛?经过博主多次WR发现不是的。有一种情况就是这个明星牛群是一个有向环,有向环?肯定要想到强连通分量!他们彼此相互崇拜。
同理,整个牛群中有可能存在多个有向环。真的是搞不懂难道他们还自己崇拜自己?
到这里,这个题的大概思路就出来了。求出牛群中有多少个强连通分量,把每个强连通分量都压缩成一个点,整个一个牛群间的关系就可以转化成一个有向无环图,即为DAG。
然后找明星牛,明星牛所在的压缩点的出度一定是为零的~。
如果有多个压缩点的出度都为零了呢?我一开始没有考虑这里,又WR了好几次。当有多个压缩点的出度为零,那么很显然,整个牛群中没有明星牛!
pass:这个题的数据poj出的有点弱,好像数据里没有考虑孤立结点的情况,给一组数据,博主没有过,但是提交的时候AC了,下面的代码是改正过的代码。
这里求强连通分量我用的是trajan算法
input
4 3
1 2
2 1
1 3
/*4是孤立节点*/
output
0
下面是完整的代码
#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
const int vMaxsize = 10050;
const int eMaxsize = 50050;
typedef struct
{
int s,e,next;
}node;
node edge[eMaxsize];
int head[vMaxsize],dfn[vMaxsize],low[vMaxsize],color[vMaxsize];
bool vis[vMaxsize],judge[vMaxsize];
int e_num,scc,index;
void addedge(int a,int b)/*加边*/
{
edge[e_num].s = a;
edge[e_num].e = b;
edge[e_num].next = head[a];
head[a] = e_num++;
}
void getmap(int n,int m)/*建图*/
{
memset(head,-1,sizeof(head));
memset(vis,false,sizeof(vis));
memset(dfn,-1,sizeof(dfn));
memset(low,0,sizeof(low));
memset(judge,false,sizeof(judge));
e_num = 0;
while(m--)
{
int a,b;
scanf("%d %d",&a,&b);
judge[a] = true;
judge[b] = true;
addedge(a,b);
}
}
stack<int>s;
void trajan(int u)/*求强连通分量*/
{
dfn[u] = low[u] = index++;
vis[u] = true;
s.push(u);
for(int i = head[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].e;
if(dfn[v] == -1)
{
trajan(v);
if(low[u] > low[v]) low[u] = low[v];
}
else if( vis[v] && low[u] > dfn[v])
{
low[u] = dfn[v];
}
}
if(low[u] == dfn[u])
{
int j;
scc++;
do
{
j = s.top();
s.pop();
vis[j] = false;
color[j] = scc;/*不同的强连通分量染色*/
}while(u!=j);
}
}
void solve(int n)
{
scc = 0;
for(int i = 1; i <= n ; i++)
{
if(dfn[i] == -1)
{
trajan(i);
}
}
}
int main()
{
int n,m;
while(scanf("%d %d",&n,&m)!=EOF)
{
int in[vMaxsize],out[vMaxsize];
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
getmap(n,m);
solve(n);
int key = 0;
for(int i = 1; i <= n ; i++)/*确定是否有孤立节点*/
{
if(judge[i] == false)
{
key++;
}
}
for(int i = 0 ; i < e_num ; i++)
{
if(color[edge[i].s] != color[edge[i].e])
{
out[color[edge[i].s]]+=1;/*存储各个压缩点的出度跟入度*/
in[color[edge[i].e]]+=1;
}
}
int sum = 0;
bool flag = false;
if(key != 0)/*有孤立节点,则一定没有明星牛*/
{
sum = 0;
}
else if(scc == 1)/*整个牛群只有一个强连通分量,一定是彼此相互崇拜的*/
{
sum = n;
}
else
{
for(int i = 1; i <= scc ; i++)
{
if(out[i] == 0 && in[i] != 0)
{
for(int j = 1; j <= n ; j++)
{
if(color[j] == i)
{
sum++;
}
}
if(!flag)/*标记,是否只有一个压缩点的出度为零*/
{
flag = true;
}
else
{
sum = 0;
break;
}
}
}
}
printf("%d\n",sum);
}
return 0;
}
下面给一些数据,可以去测测自己的code
input
3 3
1 2
2 3
3 1
3 3
1 2
2 1
2 3
5 4
1 4
2 4
3 4
5 4
5 5
1 2
2 3
3 1
1 4
4 5
5 6
1 2
2 3
3 1
1 4
4 5
5 3
2 2
1 2
2 1
3 2
1 2
2 1
6 6
1 2
2 3
3 1
1 4
4 5
5 3
5 6
1 2
2 3
3 1
1 4
4 5
5 4
5 7
4 1
1 2
2 3
3 1
1 4
4 5
5 4
5 6
1 2
2 3
3 1
1 4
4 5
5 1
7 9
1 2
2 3
3 1
4 5
5 6
6 4
4 7
7 1
1 7
6 6
1 2
2 3
3 1
4 5
5 6
6 4
4 4
1 2
2 3
3 1
1 4
4 4
1 2
2 3
3 1
4 1
5 6
1 2
2 3
3 1
5 1
5 4
3 4
7 9
1 2
2 3
3 1
5 1
5 4
3 4
4 7
7 6
6 4
output
3
1
1
1
5
2
0
0
2
5
5
4
0
1
3
1
3