codeforces 贪心 Number Game

题目描述:

 题目大意:由Alice决定比赛的轮数 k , 第 i 轮 ( 1 <= i <= k), Alice 每次从数组中去除任意一个小于等于 k - i + 1的数,之后Bob对剩余数中任意一个加 k - i + 1。问 Alice 能保证自己必胜的最大 k 值是多少。

算法:博弈 & 贪心

        第一点 : 假设数组中有 x 个 1,那么轮数就不能超过 x

        证明比较简单,如果 x 个 1,我们要进行 x + 1轮比赛,那么Bob 每次只需要对这些 1 进行加法,然后最后一轮的时候,数组中已经不存在 1 了,而最后一轮 Alice 是要去除 小于等于 1 的数,所以Alice 就必败。

        第二点: 对于 Bob 每个操作了的数,Alice 都不会在之后的轮数中去除它。

        由于数组中的数 ai 都是属于 [1,n] 的,所以 Bob 在一轮的末尾对一个数加上 k - i + 1, 那么在下一轮这个数肯定不会小于等于 k - (i+1) + 1。

        假设 1 的个数为 x ,那么轮数至多为 x 。 并且比赛的最后一轮,Alice 肯定是去除 1 的。那么在前 k - 1 轮,Bob 在干些什么呢?

        由于 Bob 每个操作了的数,Alice 都不会在接下来的轮数中去除它,所以Bob 肯定是优先为那些最小的数进行加法,这样他为Alice腾出的选择的空间就越小,也就是说 Bob 在前 k - 1 轮,都是在取 1 做加法。(我们有足够的 1, 因为轮数最大为 1 的个数,这是一个上界)。

        那么前k - 1轮 Bob 在疯狂取 1, Alice 呢?

        我们计 nums[i] 为 i 在数组中出现的次数, 那么 nums[1] + nums[2] + ..... nums[i] 就是小于等于 i 的数个数总和。

        对于第 i 轮, Alice都可以去除一个 小于等于 k - i + 1 的数,也就是说第一轮的时候小于等于 k 存在一个, 第二轮的时候小于等于 k - 1 存在1 个 .....倒数第 2 轮的时候小于等于 2 存在一个......

        我们先看最后一轮,Bob 取了 k - 1 个 1,所以数组中还剩下至少一个 1,也就是 1 + k - 1 <= nums[1] -----> k <= nums[1],  这 k 个 1我们看成是固定的。

        倒数第二轮,由于有 k 个 1是不能够被 Alice 利用的(这 k 个 1的用途如上),此时还需要满足至少有 1 个数小于等于2,小于等于 2 的总数为 nums[1] + nums[2].  所以 nums[1] + nums[2] - k >= 1。

        倒数第三轮,Alice 要找到一个小于等于3的数,除此之外还必须存在一个小于等于 2 的数,也就是说,除了那 k 个 1 之外, 必须有两个数 小于等于3 ,并且其中一个数还要小于等于2. 也就是说 nums[1] + nums[2] + nums[3] - k >= 2 && nums[1] + nums[2] - k >= 1

        那么可以递推到 第 1 轮(第1轮到第 k - 1轮)。第一轮时,除了那 k 个1 (用途为第 k 轮Alice 拿去的那个 1 和 Bob 进行操作的 k - 1 个 1),我们必须有1个数小于等于k, 并且除这个数之外,还有1个数小于等于 k - 1, 并且除以上 2 个数之外,还至少存在1个数小于等于k - 2.....也就是我们可以列出如下不等式:

        nums[1] + nums[2] +........ nums[k] - k >= k - 1

        .........

        nums[1] + nums[2] - k >= 1

        也就是说:

        k <= nums[1]     &&                                                            一级条件 

        k <= nums[1] + nums[2] - 1 &&                                          二级条件

        ............                                         &&

        k <= nums[1] + nums[2] +.....nums[k] - (k+1)                      k 级条件

     AC代码:

using namespace std;
#include<vector>
#include<algorithm>
#include <numeric>
#include <string>
#define ll long long 
#include<set>
#include<bitset>
#include<iostream>

void solve() {
	int n; cin >> n;
	vector<ll> a(n, 0); vector<ll> nums(n + 1, 0);
	for (int i = 0; i < n; i++) {
		cin >> a[i]; nums[a[i]]++;
	}

	ll z = nums[1];
	ll ans = z, s = z;
	
	for (ll i = 2; i <= z; i++) {
		s += nums[i];
		// s 表示 1 - i 的总个数
		ll y = s - (i - 1);
		z = y;
		ans = min(ans, y);
	}
	cout << ans << endl;
}

int main() {
	int t;
	cin >> t;
	while (t--) {
		solve();
	}
}

        

        

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值