O(logn)解法:
根据题目进行分析,不难发现2^n个瓶子可以合并到一个瓶子里(此n非彼n,不是题目里的n,而是一个任意的数),于是可以想到可以把n(题目所给的n)拆成多个2的次方相加的形式,即把n转换成二进制,然后统计有多少个1,1的个数就是不买新瓶子时所能达到的最优情况中瓶子的个数(即把瓶子合并成最少时瓶子的个数),用一个变量num来记录这个数。如果num<=k,那么就不用再买新瓶子,如果num>k,我们就需要通过买新瓶子来减少1的个数。拿第二个样例来分析,n=13,k=2,把n转化成二进制:1101,为了使买的瓶子最少,我们从最小位(最右边的位)往左扫,如果当前i位置对应的二进制值为1,那我们就把n加上2^(i-1)(为什么是i-1?因为二进制中从右往左数第i位对应的值就是2^(i-1)),这样二进制数就变成了1110,可以发现1的个数并没有减少,但是继续往左扫,当i=2时,第i位(以后第几位都是指从右往左数)的二进制值为1,那就给n加上2^(2-1),此时n变成了10000,1的个数就减少了。此时1的个数为1,小于k,于是答案为2^(1-1)+2^(2-1)=3。不难发现,当第i位为1时,左边与它相连的(不包括他本身)有多少个1,在加上2^(2-1)后就会减少多少个1,这就是解本题的原理。也许你会想,当n=13时,直接加上2^(3-1)不就满足条件了吗,为什么一定要从右往左扫?可以发现2^(3-1) > 2^(1-1)+2^(2-1),所以从右往左扫依然最优。并且这个性质具有通性:2^i > ∑ 2^j(0<j<i)。
#include <iostream>
#include <cstdio>
using namespace std;
int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=x*10+c-'0';
c=getchar();
}
return f*x;
}
int n,k,ans,num,pre1,pre2;
int main()
{
n=read(); k=read();
for(int i=0;i<=31;i++) if((n&(1<<i))) num++;
for(int i=0;i<=31;i++)
if((n&(1<<i)))
{
if(num<=k) break;
ans+=(1<<i);
pre1=i; pre2=k;
while((n&(1<<(i+1))))
{
i++;
num--;
if(num<=k) break;
}
n+=(1<<pre1);
if(num<pre2) i--;
}
cout<<ans;
return 0;
}