一、倒水

【题目描述】

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

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

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

【输入】

     一行两个正整数NK1≤N≤109    K1000);

【输出】

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

【样例输入】

3 1
【样例输出】
1

【数据规模】

对于30%的数据,N≤3*105

对于100%的数据,1≤N≤109  K1000

分析:

    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.
View Code