您的位置:首页 > 其它

GDUT2017校赛:Problem E: 倒水(Water)(思维,二进制)

2017-03-28 13:48 387 查看


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^9,K<=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,将低位的
4000
两个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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐