【洛谷】P1582 倒水(二进制数)题解

原题地址:https://www.luogu.org/problem/P1582

题目描述

一天,CC买了N个容量可以认为是无限大的瓶子,开始时每个瓶子里有1升水。接着~~CC发现瓶子实在太多了,于是他决定保留不超过K个瓶子。每次他选择两个当前含水量相同的瓶子,把一个瓶子的水全部倒进另一个里,然后把空瓶丢弃。(不能丢弃有水的瓶子)

显然在某些情况下CC无法达到目标,比如N=3,K=1。此时CC会重新买一些新的瓶子(新瓶子容量无限,开始时有1升水),以到达目标。

现在CC想知道,最少需要买多少新瓶子才能达到目标呢?

输入输出格式

输入格式:

一行两个正整数, N,K(1≤ N ≤ 2×10^9,K ≤ 1000)

输出格式:

一个非负整数,表示最少需要买多少新瓶子。

输入输出样例

输入样例#1:

3 1

输出样例#1:

1


输入样例#2:

13 2

输出样例#2:

3


输入样例#3:

1000000 5

输出样例#3:

15808


说明

时空限制:1000ms,128M

思路:一道二进制数的题。题意就是合并成的瓶子(即相应二进制下1的个数)如果超过了k瓶子,则最少需要购买多少个瓶子使得再次合并后的瓶子数目小于等于k。

  1. 首先需要知道,a&-a 表示返回的值就是该数 a 在二进制下从后往前数,到第一个 1 出现为止的数。那么可以定义一个函数 merge 计算合并后的瓶子数目。
  2. 当 n 个瓶子合并后,瓶子数目超过了 k 个瓶子,则购买 n&-n 个瓶子,使得 n 在二进制下在最后一位 1 再添上个 1 ,进位,水杯的个数只会减少(或不变),而不会增多。同时记录增加的水杯数,以及n加上购买的水杯数。
  3. 最后水杯数合并后的数目小于等于 k 即可停止,输出购买的水杯数,即为最少的水杯数。

代码如下:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int merge(int a){	//计算合并成的瓶子个数 
	int sum=0;	//初始化为0 
	while(a){	//当该数不为0 
		sum++;	//数目加1 
		a-=(a&-a);	//减掉该数二进制下最后一个1 
	}
	return sum;
}
int main(){
	int n,k,total=0;	//total表示至少需购买的瓶子数量 
	cin>>n>>k;
	while(merge(n)>k){	//当合并成的瓶子数目大于限定的瓶子数 
		total+=(n&-n);	//total加上此次购买的最少瓶子数 
		n+=(n&-n);		//加上购买的瓶子数,进位 
	}
	cout<<total<<endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值