P1582 倒水 (二进制)

题目描述

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

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

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

输入输出格式

输入格式:

一行两个正整数, N,K( 1le Nle 2 imes 10^9,Kle 10001N2×109,K1000 )。 

输出格式: 

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

输入输出样例

输入样例#1: 
3 1
输出样例#1: 
1
输入样例#2: 
13 2
输出样例#2: 
3
输入样例#3: 
1000000 5
输出样例#3:
15808

Solution

二进制水题一道.

因为只有当有两个相同的水瓶时才可以合并;

所以可以很快推出一条性质:

每个合并出来的水瓶最终水量必为 2 的 n 次方.

然后也就是说,把当前这个数转化成二进制,它共有多少位是 1 , 也就是它可以合并出来的水瓶.

因为只能加,所以我们就一步一步把这个数字跳为恰有 k 个1的最小值即可.

关于取一的个数;

树状数组引用过一个 x&(-x) 便能取出末尾的1.

每次取完减去即可,直到为0.

代码

#include<bits/stdc++.h>
using namespace std;
int n,k,ans;
int pal(int x)
{
    int num=0;
    while(x)
    {
        num++;
        x-=x&-x;
    } 
    return num;
}
int main(){
    scanf("%d%d",&n,&k);
    while(pal(n)>k) 
    ans+=n&-n,n+=n&-n;
    cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/Kv-Stalin/p/9069593.html