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