题目链接:
http://poj.org/problem?id=3660
题目大意:
有n头牛,给定n头牛的先后关系,按照这个先后关系给n头牛排序。
当然,有些牛的位置是可以确定的,而有些牛的位置是不确定的。求能确定位置的牛的头数。
解题思路:
采用拓扑排序。
当前状态,如果入度为0的点不止一个,那么很显然,这些点都不能确定位置。
如果入度为0的点的只有一个,那么我们就需要判断,该点的祖先的个数是否等于已经删除的点的个数,
如果相等的话,那么这个点的位置就是可以确定的。
寻找祖先节点的方法比较暴力,用的是弗洛伊德算法。
注意:弗洛伊德算法三重循环的顺序,第1重循环必须是枚举中间节点,这样才能保证所有的点对都能被更新。
源代码:
//先用弗洛伊德法求解每个节点的祖先个数
//然后进行拓扑排序
//采用邻接矩阵存储
//这个题目还要考虑出现了环的情况
#include<stdio.h>
#include<stdlib.h>
#include<string>
#include<string.h>
#include<math.h>
#include<cmath>
#include<vector>
#include<map>
#include<iostream>
#include<algorithm>
using namespace std;
int s[105][105];
int s1[105][105];
int d[105];
int pre[105];
int son[105];
vector<int> v;
int n,m,ans;
void Floyd()
{
int i,j,k;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
s1[i][j]=s[i][j];
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) //中间点
if(s1[i][k] && s1[k][j])
s1[i][j]=1;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(s1[i][j])
{
pre[j]++;
// son[i]++;
}
// ans=0;
// for(i=1;i<=n;i++)
// if(pre[i]+son[i]==n-1)
// ans++;
// printf("%d\n",ans);
return;
}
void top_sort()
{
int i,j,k,t,len,cnt;
cnt=ans=0;
while(1)
{
v.clear();
for(i=1;i<=n;i++)
if(d[i]==0)
v.push_back(i);
len=v.size();
if(len==0) //没有找到这样的点,直接退出
break;
k=v[0];
//如果当前只有一个点的度数为0,并且该点祖先的个数等于已经删掉的点的个数,那么该点的位置是可以确定的
if(len==1 && pre[k]==cnt)
ans++;
cnt+=len;
for(i=0;i<len;i++)
{
k=v[i];
d[k]=-1;
for(j=1;j<=n;j++)
if(s[k][j])
d[j]--;
}
}
printf("%d\n",ans);
return;
}
int main()
{
freopen("in.txt","r",stdin);
int a,b,i,j,k,t;
while(scanf("%d%d",&n,&m)==2)
{
memset(s,0,sizeof(s));
memset(d,0,sizeof(d));
memset(pre,0,sizeof(pre));
memset(s1,0,sizeof(s1));
memset(son,0,sizeof(son));
while(m--)
{
scanf("%d%d",&a,&b);
if(!s[a][b]) //考虑重边的情况
d[b]++;
s[a][b]=1;
}
Floyd(); //采用floyd算法计算出每个节点祖先的个数
top_sort(); //然后进行拓扑排序
}
return 0;
}