一、倒水
【题目描述】
一天,树树买了N个容量可以认为是无限大的瓶子,初始时每个瓶子里有1升水。树树发现瓶子实在太多了,于是他决定保留不超过K个瓶子。每次他选择两个当前含水量相同的瓶子合并,把一个瓶子的水全部倒进另一个瓶,然后把空瓶丢弃(不能丢弃有水的瓶子)。
显然在某些情况下树树无法达到目标,比如N=3,K=1。此时树树会重新买一些新的瓶子(新瓶子容量无限,开始时有1升水),以达到目标。
现在树树想知道,最少需要买多少新瓶子才能达到目标呢?
【输入】
一行两个正整数N,K(1≤N≤109, K≤1000);
【输出】
一个非负数,表示最少需要买多少新瓶子。
【样例输入】
3 1
【样例输出】
1
【数据规模】
对于30%的数据,N≤3*105;
对于100%的数据,1≤N≤109, K≤1000
分析:
n可以表示为二进制相加的形式: n=2^x1+2^x2+2^x3+......2^xm (x1<x2<x3<......<xm),即,n转换为二进制数里有m个1则此时最少要保留m个瓶子。
如果M<k,则不需要新瓶子。
如果M>k,则首先消掉二进制中最低位(x1+1)位的1,则需要添加2^x1个新瓶。依次类推直到二进制中“1”的个数减少到K个或以下停止。
求某个十进制下的数字在二进制下有多少个“1”,可以用不断减lowbit的方法来求。
假设n的二进制中最后一个“1”的位置为右数第(x1+1)个,则lowbit(n)=2^x1。
n的lowbit可以用 n and (-n)求出来
因为:在计算机系统中,数值一律用补码来表示和存储。正整数的补码与原码相同,求负整数的补码,符号位不变,数值位各位取反,最后整个数加1。
比14的补码为00001110,;-14的补码为11110001+1=11110010;则14 and(-14)=00000010
//x and (-x) 取二进制的最末位 var n,k,t,j,i,ans:longint; a:array[1..100] of integer; function solve(n:longint):longint; var num:longint; begin num:=0; while n>0 do begin n:=n-(n and(-n)); inc(num); end; exit(num); end; function power(x:longint):longint; var sum:integer; begin for i:=1 to x do sum:=sum*2; end; begin assign(input,'water.in');reset(input); assign(output,'water.out');rewrite(output); readln(n,k); ans:=0; while solve(n)>k do begin ans:=ans+(n and (-n)); n:=n+(n and (-n)); end; writeln(ans); close(input);close(output); end.