题目描述
小赛是一名幸运的程序员。
虽然他成功帮助小朋友以最快时间夺回了狼堡,但是面试官却打算和他说拜拜了。
理由是——游戏天赋太高,有不务正业、走火入魔的倾向QAQ……
尽管小赛很不能接受这个理由,可是却只能心灰意冷地吃下这个结果。
然而,在他即将走出门的时候,面试官给了幸运的小赛一个最后的机会。
原来,面试官的手机被他调皮的儿子小明用一个数字作为密码锁上了。
小明只记得这个数字的十进制范围是l~r,且这个数的二进制表示中恰有m个1,却不记得确切的数字了。
面试官可急坏了。这才有了小赛一个将功赎过的机会。
他想要让小赛算出,他最坏情况下,要试多少次密码才能确保打开手机呢?
请输出这个次数。
解析:
如果遍历所有的l - r的数,并计算‘1’的数目,统计符合条件的数的数目,这种方法的效率低。
如果考虑由m个‘1’能构成多少个在l - r之间的数,则计算时间将大大减低,该算法相当于采用递归的方式遍历树,并统计有效叶节点(符合条件的)。
参考代码:
#include <stdio.h>
#include <math.h>
int cout = 0;//计数
int tt[24]; //用于保存二进制数每个‘1’所能代表的数
int delt(int max, int l,int r ,int w ,int starts,int ends){
if (w <= 0 && max>=l && max <=r){
cout++;
return 0;
}
if (w<=0)return 0;
for(int i=starts;i<=ends ;i++ )delt( max-tt[i], l ,r ,w-1,i+1,ends);
}
int value(int l,int r ,int m){ //计算可能有多少个数含m个1
int w = 0; //位宽增量
while(m+w < 25 && tt[m+w-1] <= r ){//由于题目给出了输入范围,可以限定总的位宽 -1>= l
delt( tt[m+w] -1, l ,r ,w,0,m+w-2); // 递归计算
w+=1;
}
if (cout<=0) cout =-1;
return cout;
}
int main(int argc, char** argv) {
int l,r,m;
for(int i=0;i<25;i++) tt[i]=pow(2,i); //初始化二进制数,每位‘1’所代表的数值
while(l){
cout = 0;
scanf("%d %d %d",&l,&r,&m); //获取输入
printf("%d\n", value(l,r,m)); //
}
return 0;
}