题目描述:
小蓝最近在学习二进制。他想知道 1 到 N 中有多少个数满足其二进制表示
中恰好有 K 个 1。你能帮助他吗?
输入格式:
输入一行包含两个整数 N 和 K。
输出格式
输出一个整数表示答案。
样例输入
7 2
样例输出
3
评测用例规模与约定
对于 30% 的评测用例,1 ≤ N ≤ 106, 1 ≤ K ≤ 10。
对于 60% 的评测用例,1 ≤ N ≤ 2 × 109, 1 ≤ K ≤ 30。
对于所有评测用例,1 ≤ N ≤ 1018, 1 ≤ K ≤ 50。
本题思路
可以用dp,或者转化成记忆化搜索
这边参考了二进制问题(第十二届蓝桥杯国赛cb组)数位dp(记忆化搜索、递推 详细思路)_哥本哈根不养猫的博客-CSDN博客_蓝桥杯二进制问题z
这位博主写的非常好
我使用的是记忆化搜索,我不是很会dp,大部分喜欢搜索,对dp有兴趣的可以看看上面的链接
本题其实就是突破如何建立搜索,其实每一位无非两个状态0,1,1018次方,也不过就是2的64,也就是最多无非64位,其实是完全可以搜索,转化成二进制,主要是要有减枝的思想,加上如何突破列举出的数不大于给的数,这边给出的是一个状态,如果和原本的数位不完全一样的话,就不回大于,具体可以参见代码,我们不是每一位都给两个状态,如果原位给是0,是不会给1的,这样的话后面出现的所有情况都不可能是大于它的
下面上代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
public class Main二进制问题 {
static long dp[][]=new long[101][101],k;
static int a[]=new int[101];
public static void main(String[] args) throws IOException {
BufferedReader x=new BufferedReader(new InputStreamReader(System.in));
PrintWriter out=new PrintWriter(System.out);
String ss[]=x.readLine().split(" ");
long n=Long.valueOf(ss[0]);
k=Long.valueOf(ss[1]);
for(int i=0;i<101;i++)//初始化全为-1
Arrays.fill(dp[i], -1);
int l=0;//记录位数
while(n!=0) {
a[++l]=(int)(n%2);//传化成二进制
n/=2;
}
out.println(dfs(l,0,true));//搜索
out.flush();
}
public static long dfs(int l,int lp,boolean f) {//长度l下lp个1
if(l==0) return lp==k?1:0;//如果到达最后一位,1的数量等于k
if(!f&&dp[l][lp]!=-1) return dp[l][lp];//如果搜索过,且不和原本2进制数位相同的话,直接返回,记忆化搜索
int n=f?a[l]:1;//这边如果和原本2进制相同就等于原本位,因为我们要限制不能大于给的数,
//所以如果前面都和原本相同,且这位为0那么如果给1的话就大于给的数,就会出错,这就是巧妙之处
long sum=0;
for(int i=0;i<=n;i++)//开始搜索
sum+=dfs(l-1,lp+i,f&&i==a[l]);
return dp[l][lp]=sum;
}
}
如有侵权,联系删