题目描述:小D想按照奶牛产奶的能力给她们排序。现在已知有N头奶牛(1 ≤ N ≤ 1,000)。小D通过比较,已经知道了M(1 ≤ M ≤ 10,000)对相对关系。每一对关系表示为“X Y”,意指X的产奶能力强于Y。现在小D想要知道,他至少还要调查多少对关系才能完成整个排序。
输入:第1行包含两个整数N和M。第2 … M+1行,每行都包含两个整数X和Y。X和Y都在1~N范围内,表示奶牛X的排名高于奶牛Y。
输出:单行输出至少还要调查多少种关系才能完成整个排序。
输入样例:
5 5
2 1
1 5
2 3
1 4
3 4
输出样例
3
提示:在输入样例中,cow2>cow1>cow5,cow2>cow3>cow4,所以cow2的排名最高。不过,小D要知道排名cow1及cow3的排名第二的牛,还需要通过一个问题来确定cow4和cow5的顺序。之后,他要知道如果cow1大于cow3,那么cow5是否大于cow3,他必须问三个问题才能确定排名cow1>cow3 cow4>cow5 cow5>cow3
题解:根据输入样例,创建一个有向图:
(2)、根据传递性,得到的已知关系有7种,分别是:1>4、1>5、 2>1 、2>3、 2>4 、2>5 、3>4。
(3)、对于有n个节点的图,两两之间的关系一共有n(n-1)/2种,5个节点共有
5*4/2=10种关系,还需要知道10-7=3种关系即可。
如何得到已知的关系,用位运算,将每个节点都用一个bitset来表示。
初始化时,p[i][i]=1,即p[i]的第i位为1(从右侧数第0位、1位、2位)
输入1-5,令p[1][5]=1,则表示p[1]的第5位是1,即…100010
输入1-4,令p[1][4]=1 即…110010
输入2-1,令p[2][1]=1,则表示p[2]的第1位是1,即…000110
输入2-3,令p[2][3]=1, 即…001110
…
判断每个数组的每一位,代码如下。
if(p[i][k])
p[i]|=p[k];按位或运算
注解:
例如,p[2][1]=1,则p[2]=p[2]|p[1]=001110|110010=111110
p[2][1]=1,表示2>1, 则p[2]表示2大于的节点的集合,p[1]表是1大于的节点的集合
p[2]|p[1]就表示2节点都大于1节点大于的节点,如上样例,1>5、1>4,所以可以得出
2>5、2>4
从p[2]|p[1]的j结果111110看出,2和1有关系,2和3、4、5都有关系。
通过上述方法,可以找到每个点和其他点的关系。用ans累计每个数组元素1的个数,因为初始化时自己到自己为1,所以ans多算了n种关系,所以关系数应为ans-n,用n(n-1)/2-(ans-n)就是要找的答案。
代码如下:
#include<iostream>
#include<bitset>
using namespace std;
const int maxn=1000+5;
bitset<maxn>p[maxn];
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
p[i][i]=1;
while(m--){
int u,v;
cin>>u>>v;
p[u][v]=1;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
if(p[i][k])
p[i]|=p[k];
int ans=0;
for(int i=1;i<=n;i++)
ans+=p[i].count();
cout<<n*(n-1)/2-ans+n<<endl;
return 0;
}
注:文章根据陈小玉老师算法训练营一书整理。