CF-Round#638-div2-D题

CF-Round#638-div2-D题

D. Phoenix and Science

传送门

这道题贪心+数学~

给的题解的思想真的优美!!!

本题大意:一个细胞的质量为m,细胞有分裂能力,可以在白天进行一次分裂,分裂的原则是一个细胞分裂成为两个细胞,两个细胞的质量都是m / 2;细胞在夜晚会增加一个单位的质量。分裂是自愿的,但是夜晚增加就是强制性的。现在励志当科学家的小朋友想知道有没有可能用最少的时间(天数)获得到输入所给定的细胞数n。
输出最少天数
并且输出每天选择分裂的细胞数

贪心策略:我们想想不管一个细胞分没分裂,其实总的质量是不发生变化的,质量发生变化的是在夜晚期间。
那么我们考虑夜晚期间有多少质量会增加,通过夜晚增加的质量,我们可以获得到白天有多少细胞选择分裂了。
我们假设a[0] = 1;
因为原始给定了质量为1的细胞;
是从第一天开始的。
我们构造数组a[];
a[i]表示第i天的夜晚细胞增重的质量是多少。同时也代表了第i天的细胞有多少个。
现在考虑增重的范围。
a[i] <= a[i + 1] <= 2 * a[i];
这个式子代表什么?
假设第i天晚上增重的质量是a[i],那么第i + 1天的白天不管有没有细胞分裂,考虑极限情况:
case1. 没有一个细胞分裂,那么我们第i + 1天晚上的增重的质量还是a[i];
case2. 所有细胞都分裂,那么我们就获得了2 * a[i]个细胞。那么我们第i + 1天晚上增重的就是2 * a[i];

我们需要获得一个最少的天数。
题目要求我们输出每天选择分裂的细胞数。
按照我们上面数组的规定:我们只需要作差就可以获得到选择分裂的细胞数了,比如:a[i + 1] - a[i]就是代表第i天的白天选择分裂的细胞数。
至于最少天数,我们首先就是尽可能地让所有细胞全部都分裂。
找到最快的方案。
所以,这就与2有关联了,每次要乘2.
我们不妨构造2的次幂的数组,最大可能的分裂,看看n是否与2的某一次幂相等。
a[] = {1, 2, 4, 8, 16, …};
上面说了是净增加的质量,那么我们可以通过求前缀和来获得到总质量,对比n,获得到细胞全部分裂的最大天数。
看看有没有多的部分(没有的话那最好,直接作差输出即可)

至于多的部分,就需要我们把多的部分直接加到数组a[]中,因为如果进行下一次分裂,就已经超过了我们的质量n了,不满足题目要求。
所以这多出来的部分我们要把它放在前面的某个地方,缓冲一下,那一天不让细胞全部分裂,分裂一部分或者不分裂。这个多出来的部分也就是净增加的质量,所以可以直接放进去,至于放在哪个位置。因为细胞质量只可能增加不可能减少,所以我们要把多出来的部分放在第一个大于这个数的前面一个位置即可。这里我们直接sort一下非常方便~

题外话:因为用2进制可以表示任何数字,所以本题不存在输出-1的情况。也就是所有给定的n都是可以进行输出方案的~

代码部分:

#include 
using namespace std;

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		int n;
		vector<int> v;
		cin >> n;
		for (int i = 1; i <= n; i *= 2)
		{
			v.push_back(i);
			n -= i;
		}
		if (n > 0)
		{
			v.push_back(n);
			sort(v.begin(), v.end());
		}
		int siz = v.size();
		cout << siz - 1 << endl;
		for (int i = 1; i < siz; i++)
		{
			cout << v[i] - v[i - 1] << " ";
		}
		cout << endl;
	}
	return 0;
}

你可能感兴趣的:(贪心)