经典例题(一)——倒水
【题目描述】
小张买了n个容量可以认为是无限大的瓶子,开始时每个瓶子里有1升水。接着,小张觉得瓶子实在太多了,于是他决定保留不超过k个瓶子。每次他选择两个当前含水量相同的瓶子合并,把一个瓶子的水全部倒进另一个里,然后把空瓶丢弃。(不能丢弃有水的瓶子)
显然在某些情况下小张无法达到目标,比如n=3,k=1。此时他会重新买一些新的瓶子(新瓶子容量无限,开始时有1升水),以到达目标。
现在树树想知道,最少需要买多少新瓶子才能达到目标呢?
【输入文件】
一行两个正整数,n,k(1<=n<=109,k<=1000)。
【输出文件】
一个非负整数,表示最少需要买多少新瓶子。
【样例输入】
3 1
【样例输出】
1
【数据规模】
对于30%的数据,n<=3*10^5
对于100%的数据如题目。
做完了再去看:
【解题思路】
定义一些变量什么的。就不再赘述了,直接上代码。
var n,t,i,j,k,x,y:longint; a:array[0..51] of longint;
首先,举几个例子试一试。不难发现,若n为2a时一个多的都不用。
Function check_sit:boolean; var nn:longint; begin nn:=n; if nn=1 then begin write('0'); close(input); close(output); halt; end; while nn<>1 do begin if (nn<>1) and odd(n) then exit(False); nn:=nn div 2; end; end;
其他情况要好好思考了,其实也是可以看出一些规律来的。比如带一个数13;
发现13=1+4+8,所以可以联想到2进制,即(13)10=(1101)2
代码如下:
Procedure doit; begin k:=n; while k>0 do begin x:=x+1; a[x]:=k mod 2; k:=k div 2; end; k:=0; for i:=x+1 downto 1 do begin if (a[i]=0)and(k<=t-1) then y:=i; if a[i]=1 then k:=k+1; end; if k<=t then writeln(0) //寻找二进制中最靠前的0,然后将这个0改为1并将这个0之后的一段截取并转换为十进制s。算出一个最接近并且大于这个数的2的幂次m,用m减去s,即为答案 else begin k:=1 shl (y-1); x:=k-n mod k; writeln(x); end; end;
接下来不用多说了,就剩下调用子程序了,上主程序:
Begin assign(input,'water.in'); assign(output,'water.out'); reset(input); rewrite(output); readln(n,t); if not check_sit then doit else write('0'); //注意:如果是2的幂则输出True否则输出False,因为这里是启动一般情况的子程序,所以当check_sit返回False执行即可 close(input);close(output); End.
测试数据相关:【提供数据量最大的一组】
In:536870919 1
Out:536870905