奶牛排序(POJ3275)

题目描述:小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;	
}

注:文章根据陈小玉老师算法训练营一书整理。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值