要求:一个数的二进制数的0的个数大于等于1的个数,则这个数称为"round number",问[a,b]有多少"round number"。
方法:数位dp 记忆化搜索
1.dp[i][j][k]示i位数 j个0 k个1的个数。
2.套路记忆化搜索即可,细节在代码中说明。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <map>
using namespace std;
long long dp[40][40][40] ;
//dp[i][j][k]示i位数 j个0 k个1的个数
int digit[40] ;
long long dfs(int len , int num0 , int num1 , int lim , int bz)
{
//len 表示当前的位数
//num0 记录遍历到当前位时前面的0出现的个数
//num1 记录遍历到当前位时前面的1出现的个数
//lim 当前位是否有上限
//bz 是否前面已经有1 若前面没有1 则num0不会增加
int i , j ;
int num ;
int ans = 0 ;
if(len <= 0)//搜索到底层
return num0 >= num1 ;
if(!lim && bz == 1 && dp[len][num0][num1] != -1)//若满足条件且已搜索过
return dp[len][num0][num1] ;
if(lim) //若有上限,则可取到这个数的此位数
num = digit[len] ;//num表示当前位的上限
else //若无上限,则可取到1
num = 1 ;
for(i = 0 ; i <= num ; i ++)
{
if(bz == 0)//前面没有1
{
if(i == 0)
ans += dfs(len - 1 , num0 , num1 , lim && i == num , 0) ; //前面没有1且当前位为0,则num0不增加。
else
ans += dfs(len - 1 , num0 , num1 + 1 , lim && i == num , 1) ;
}
else//前面有1
{
if(i == 0)
ans += dfs(len - 1 , num0 + 1 , num1 , lim && i == num , 1) ;
else
ans += dfs(len - 1 , num0 , num1 + 1 , lim && i == num , 1) ;
}
}
if(!lim && bz == 1)
dp[len][num0][num1] = ans ;//已经搜索过的不用再次搜索
return ans ;
}
long long solve(long long x)
{
int len ;
len = 0 ;
while(x)
{
digit[++len] = x % 2 ;
x /= 2 ;
}
return dfs(len , 0 , 0 , 1 , 0) ;
}
int main()
{
int t ;
int i , j , k ;
int a , b ;
int ans ;
memset(dp , -1 , sizeof(dp)) ;
scanf("%d%d" , &a , &b) ;
ans = solve(b) - solve(a - 1) ;//因为solve(x)返回的是1~x的值,因此计算a时需要传递参数a-1。
printf("%d" , ans) ;
}