清北day2 题目:集合

【问题描述】

给定一个可重集合,一开始只有一个元素0 。然后你可以操作若干轮,每一

轮,你需要对于集合中的每个元素 x 进行如下三种操作之一:

1、将 x 变为 x +1。

2 、将 x 分裂为两个非负整数 y,z,且满足 x = y + z 。

3、什么都不做。

每一轮,集合中的每个元素都必须进行上面三个操作之一。

对于一个最终的集合,你的任务是判断至少进行了多少轮。

【输入文件】

第一行为一个正整数n ,表示集合的最终大小。

第二行为n 个非负整数,描述集合中的元素。

【输出文件】

输出一个非负整数,为最少的轮数。

【输入输出样例】

multiset.in multiset.out

5

0 0 0 3 3

5

【数据规模和约定】

设集合中最大的元素为m 。

对于10% 的数据,满足n ≤10,m ≤10。

对于30%的数据,满足n ≤ 50,m ≤100。

对于50%的数据,满足n ≤1000 ,m ≤10000。

对于100% 的数据,满足1≤ n ≤1000000,0 ≤ m ≤100000

思路:挺不错的个水题,为什么这样说呢,其实就是如果一个数不是0,那就让他减一,若是0,就将俩个0合并起来,比如说这个样例:0 0 0 3 3 先将3-1,再把俩个0合并,就变成了0 0 2 2 2 ,然后再进行这样的操作,0 1 1,在进行一遍成了0 0 0,继续0 0,最后成了0,这一共进行了5轮。但是我们模拟的话,100%的数据肯定是过不了的,所以我们要想方法,怎样做呢,看代码:

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <climits>
 6 #include <cstdlib>
 7 #include <cmath>
 8 #include <queue>
 9 #include <vector>
10 #include <utility>
11 using namespace std;
12 const int MAXN = 5e6 + 60, MX = 1e6;
13 int N, a[MAXN], cnt[MAXN], res, lim;//res统计答案||lim统计里面的最大值 
14 //cnt桶排 
15 int main()
16 {
17     freopen("multiset.in", "r", stdin);
18     freopen("multiset.out", "w", stdout);
19     scanf("%d", &N);//输入最后的序列长度 
20     for (int i = 1; i <= N; ++i) {
21         scanf("%d", &a[i]);//输入最后序列的变化 
22         lim = max(lim, a[i]);//最大的值 
23         ++cnt[a[i]];//桶排 
24     }
25     int l = 0, z = cnt[0];//z记录有多少个0 
26     for (int i = 1; i <= lim; ++i) {
27         ++res; 
28         z = (z + 1) / 2;
29         z += cnt[i];
30     } //z 统计个数,进行折半 
31     for (; z > 1; z = (z + 1) / 2) ++res;//若还有剩余则直接向上取整折半就好 
32     printf("%d\n", res); 
33     return 0;
34 } 

猜你喜欢

转载自www.cnblogs.com/tpgzy/p/8974908.html