在开讲之前,首先需要明白下面的一个事实:
任何一个数都能用2的任意次方相加所得。
大家可能也都了解,正是因为它,许多题目都可以用二进制来做,并且大大降低了题目难度,所以二进制的应用我想大家都应该运用的很熟练。
接下来,我们用一个例题来说明二进制的方便可靠:
P2431 正妹吃月饼
题目中给到:
这些月饼的质量分别是 1 g , 2 g , 4 g , 8 g , 16 g . . . . 1g,2g,4g,8g,16g.... 1g,2g,4g,8g,16g.... 后面一个是前面的 2 2 2 倍。每种只有一个。
可以看出,月饼的质量都是
2
2
2 的若干次方,如果从
2
0
2^0
20 开始填
,正好填满显然是最优解,如果不可行的话,就尽量往这方面靠。
题目给出的范围是:对于 100 100 100% 的数据, A , B < = 2 63 − 1 A,B<=2^{63}-1 A,B<=263−1,其实这也就告诉我们用二进制做会相对简单,所以我们可以建个容量大于 64 64 64 的数组来存储每一个二进制位的数,并且从小到大来记录,这显然是比从大到小记录简单的,因为月饼重量后一个是前一个的2倍,所以前面的月饼全吃完也没有后面的月饼重,而数量要多得多,这岂不是更划算么?
最后一个需要注意的是,我们每次求出的答案都是从后输入的那个数得来的,它既是 m a x max max。
对于样例的解释:
输入
16
16
16
25
25
25
从小的往大的找:
先找 16 16 16的转换,
10000 10000 10000刚好满足。
再找 25 25 25 的 转换,
从 10001 10001 10001 开始:
10001 10001 10001 从小的往大的找,吃一个 1 g 1g 1g 的,符合;
变成 10011 10011 10011 后还可以再吃一个 2 g 2g 2g 的,符合;
变成 10111 10111 10111 还可以再吃一个 4 g 4g 4g 的,还是符合;
再加一个? 11111 11111 11111,超过了 25 25 25,不符合了,也不用往后找了;
最终答案即为 4 4 4 个,与样例答案一样。
M y My My C o d e Code Code
#include<iostream>
#include<cstdio>
#define MAXN 100
using namespace std;
long long a,b,ma[MAXN],mi[MAXN];
long long t,t_ma=-1,t_mi=-1,u=1;
int main()
{
scanf("%lld%lld",&a,&b);
long long bb=b,aa=a;
//以下两个循环为记录每一位是否进入,一直模2取数,这样就能得到最优解
while(a!=0){
mi[++t_mi]=a%2;
a/=2;
}//最小值
while(b!=0){
ma[++t_ma]=b%2;
b/=2;
}//最大值
for(int i=0;i<64;i++)
if(mi[i]==1)//记录最小月饼数
t++;
long long v=0;
while(bb>=aa){//判断月饼的数量是否符合题意
if(mi[v]==1)
v++;//记录
else{
aa+=u;
t++;
v++;
}//没吃的情况
u*=2;
}
cout<<t-1<<endl;//在while中会重复统计一个已经记录的一个,所以要减一
return 0;
}
通过例题,我们不难看出,有了二进制的帮助,我们解题的方法,思路都有了一定的提升,遇到一些特殊的数字,我们就可以想到二进制,从而更好地解决题目。
完结撒花~~