【每日一题】codeforces 1348D (1900) (贪心)

每日一题,坚持使我强大


今日份快乐:codeforces 1348D 传送门

明天份快乐:codeforces 463C 传送门


题目大意

刚开始有一个细胞,质量为 1。每天白天,可以选择 x 个细胞进行分裂。一个质量为 m 的细胞可以分裂为两个质量为 m / 2 的细胞。每天晚上,所有的细胞质量都会加 1。
问:最短需要多少天,细胞的总质量为 n。不存在就输出 -1。

分析

我们直接结合一个样例来看:
当 n = 80 时,我们先根据贪心的思想,每天都让所有的细胞都分裂。

天数开始123456
当前细胞数112481632
当前需要质量80797773654917
分裂后细胞数0248163264
分裂后需要质量797773654917-47

观察上表,前五天的过程中,每天对所需质量的贡献都是这天时的细胞数。解释一下,比如说第三天时,我们刚开始有 4 个细胞,白天分裂为 8 个细胞,晚上所有细胞都质量加 1 ,则一共增加 8 个单位的质量,所需质量就减少 8 个单位。
但是可以发现,最后一天所需的质量为 17,不是 2 的次幂,不能被正好减去,这就很明显,我们需要在前五天中插入一天,让细胞数增值为 17 ,使得第六天需要的细胞数成为 2 的次幂,被正好消除

天数开始123456
当前细胞数112481617
当前需要质量80797773654932
分裂细胞数01248115
分裂后细胞数0248161732
分裂后需要质量7977736549320

我们在第五天的时候变动一下,让这天只分裂一个细胞,分裂完以后有 17 个细胞,正好为所需质量贡献 17 个单位的质量,第六天需要的质量正好为 32 是 2 的次幂可以被消除。
我们为什么选择在第五天呢?因为 16 < 17 < 32,我们把 16 增值为 32一天的操作,分为 16 到 17 再到 32两天的操作,这样的好处就是用最短的时间得到 17个单位质量的贡献值。这样也正好说明了,这个题不会出现无解的情况,因为无论留下的是多少,都可以被补全。(如果留下质量 n = 0 时,不用补。不为 0 时,一定会有一个 x 满足 2x < n < 2 x+1)。
所以,这个题的思路就是,我们让细胞尽可能的全部分裂,最后不够的质量,再找到一个合适的位置插入,如何实现看代码。

代码

#include <bits/stdc++.h>
#include <vector>
using namespace std;

int main() {

	ios::sync_with_stdio(false);
	
	int ncase;
	cin >> ncase;
	
	while(ncase--){
		
		int n;
		cin >> n;
		
		vector<int>res;
		for(int i = 1; i <= n; i <<= 1){   	// 不断让所有的细胞分裂,直到所需质量小于当前细胞数
			res.push_back(i);		// res 中纪录的为每天最终的细胞数
			n -= i; 			// 每次分裂对所需质量的贡献都为 i
		}
		if(n > 0){				// 如果所需质量不为 0
			res.push_back(n);		// 把这个质量插入到结果中
			sort(res.begin(), res.end()); 	// 把 n 放到到满足 2^x < n < 2 ^ x+1 的位置
		}
		
		int len = res.size();
		cout << len - 1 << endl;		// 开始的 1 不用算
		for(int i = 1; i < len; i++) cout << res[i] - res[i-1] << " "; // 相邻两天的细胞数的差就是要增值的数量
		cout << endl;
		
	}
	
    return 0;
}

解释的不清楚的地方欢迎留言提出,同时欢迎大佬留言指正错误

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值