刷题记录:牛客NC25064Ranking the Cows

传送门:牛客

题目描述:

Each of Farmer John's N cows (1 ≤ N ≤ 1,000) produces milk at a different positive rate, and FJ would 
like to order his cows according to these rates from the fastest milk producer to the slowest.
FJ has already compared the milk output rate for M (1 ≤ M ≤ 10,000) pairs of cows. He wants to make a 
list of C additional pairs of cows such that, if he now compares those C pairs, he will definitely be able 
to deduce the correct ordering of all N cows. Please help him determine the minimum value of C for 
which such a list is possible.
输入:
5 5
2 1
1 5
2 3
1 4
3 4
输出:
3

首先,对于这道题题意的理解很容易出现错误.John需要的奶牛的关系是需要提前准备的,而不是跟着我们的关系获得进行动态改变的,举一个栗子:

6 4
1 2
2 3
4 5
5 6
这是两条链

假设我们可以动态改变的话,我们每一次都比较链的最后一个数字,这样无论结果如何我们都可以确认最小的数字,所以花费最小应该是5次.但是由于我们是无法动态改变我们的比较名单的,也就是当我们的6和3进行比较的时候,可能我们的6需要与1,2再次进行比较,需要花费3次.当我们的6比3小时,我们的5又需要和1,2,3进行比较,但是此时前面6和1,2的比较名单仍然是需要的,因为我们无法确认是否需要6与1,2进行比较!!

所以我们需要知道两两之间谁大谁小的关系.容易知道我们的关系总数为 n ∗ ( n − 1 ) / 2 n*(n-1)/2 n(n1)/2,所以此时我们应该计算出我们的已知的关系总数,然后相减即可.但是对于 a > b , b > c a>b,b>c a>b,b>c,我们可以推出 a > c a>c a>c的情况.对于这种情况,我们可以只用类似于树形dp的方式进行dfs即可.因为对于我们的父亲来说,儿子大于的所有点,父亲肯定都是大于的.

对于我们的合并关系部分,我们也可以进行优化.假设我们使用一个二维数组进行关系存储的话,对于儿子大于的所有点,我们是需要枚举的.但是我们可以使用状态压缩的方式将我们的关系进行一个压缩.也就是对于每一个点来说,开一个二进制数组,用1代表大于,那么对于关系合并来说,我们只需要使用或运算进行合并即可.此时省掉了一层循环.

但是我们的点有1000个!,直接按状压dp一样直接开数组显然是不合适的,此时需要2^1000的空间.此时我们可以请出 b i t s e t bitset bitset,这个stl类为我们提供了超长的01串,与我们直接使用 s t r i n g string string字符串类型存储01串不同, b i t s e t bitset bitset是可以进行位运算的,也就是说比我们的string类型更加方便!!

关于 b i t s e t bitset bitset我们涉及以下函数

d p [ n ] . c o u n t ( ) dp[n].count() dp[n].count(),用来输出我们的01串中的1的个数

下面是具体的代码部分:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
#include <bitset>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int n,m;
vector<int>edge[maxn];
int in[maxn];int vis[maxn];
bitset<1010>state[1010];
void dfs(int u) {
	state[u][u]=1;vis[u]=1;
	for(int i=0;i<edge[u].size();i++) {
		int v=edge[u][i];
		if(!vis[v]) dfs(v);
		state[u]|=state[v];
	}
}
int main() {
	n=read();m=read();
	for(int i=1;i<=m;i++) {
		int u=read(),v=read();
		edge[u].push_back(v);
		in[v]++;
	}
	for(int i=1;i<=n;i++) {
		if(in[i]==0) {
			dfs(i);
		}
	}
	int ans=n*(n-1)/2;
	for(int i=1;i<=n;i++) {
		ans-=state[i].count()-1;
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值