junior19

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Problem E: 倒水(Water)

Description

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

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

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

Input

第一行一个整数T,表示有T组数据。

接着T每行两个正整数, N,K(1<=N<=10^9K<=1000)

Output

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

Sample Input

33 113 21000000 5

Sample Output

1315808
思路:通过举几个数字找规律,发现若n个瓶最终可以合并为最少m个瓶子,则m为n的二进制中1的个数,比如3个瓶子可以最少合成2个,4个瓶子可以最少合成1个。于是得出结论,n个瓶子可以合成的瓶子数为[m(n), n]其中m为n的二进制1的数目。那么此题分三种情况讨论:①n<=k,瓶子数比目标合成数少,直接k-n就是最优解;②m(n)<=k,当前瓶子数已经满足了;③m(n)>k,那么将低位多余的1变0,例如n(二进制)=1011,k=2,将低位的两个1清零,同时左边补个1,结果为1100,例如n(二进制)=11111,k=3,结果为100000,此时n(m)<=k,是最优解了。

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

int one, t, n, k;
int solve(int n)
{
    int icount = 0;
    for(int i=0; i<33; ++i)
        if(n & (1<<i))
        {
            ++icount;
            n ^= (1<<i);
            if(icount == one-k+1)
            {
                n += (1<<(i+1));
                return n;
            }
        }
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        if(n <= k)
        {
            printf("%d\n",k-n);
            continue;
        }
        one = __builtin_popcount(n);//内置函数,返回n的二进制中1的数量。
        if(one <= k)
        {
            puts("0");
            continue;
        }
        printf("%d\n",solve(n)-n);
    }
    return 0;
}



posted on 2017-03-28 13:48  junior19  阅读(281)  评论(0编辑  收藏  举报