Test 6.24 T2 集合

问题描述

有一个可重集合,一开始只有一个元素 0。
你可以进行若干轮操作,每轮你需要对集合中每个元素 x 执行以下三种操作之一:

  1. 将 x 变为 x+1;
  2. 选择两个非负整数 y,z 满足 y+z=x ,将 x 变为这两个元素;
  3. 什么都不做。

给出一个最终的集合,求至少经过几轮才能得到这个集合。

输入格式

第一行一个正整数 n,表示最终集合的大小。
第二行 n 个非负整数 ai,表示最终集合中的元素。

输出格式

输出一个整数,表示答案。

样例输入

5
0 0 0 3 3

样例输出

解析

不妨倒过来思考,考虑如何通过将一个数减一和合并两个数使目标集合变成只有0的集合。对于任意两个数(a)(b),如果将两个数分开减为0再合并,那么操作次数为(max(a,b)+1),而先合并再减小为0的操作次数为(a+b+1)。显然第一种更优。推广开来,我们的最优策略即为每轮先和并所有的0,再把不是0的减1,直到只剩一个0为止。

具体实现也很简单。每次把0的数量减半,同时减小长度。预处理每个数在哪一轮会减为0,并在那一轮最后加上新的0的个数。

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 1000002
using namespace std;
int n,i,a[N],cnt[N],num,ans;
int main()
{
	freopen("set.in","r",stdin);
	freopen("set.out","w",stdout);
	cin>>n;
	for(i=1;i<=n;i++) cin>>a[i];
	for(i=1;i<=n;i++) cnt[a[i]]++;
	num=cnt[0];
	while(n>1){
		ans++;
		n-=num/2;
		num=(num+1)/2;
		num+=cnt[ans];
	}
	cout<<ans<<endl;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

原文地址:https://www.cnblogs.com/LSlzf/p/11079005.html