poj 3252 数位dp(dfs写法) 二进制0出现比1多的数字个数

题意:给定一个区间[n,m],求这个区间内的round数的数量。所谓round数,就是这个数的二进制表示(不含前导零)中0出现的个数不比1出现的个数少。前几个Round数:0,2,4,8,9,10,12。

思路:dp。dfs(int k,int hasone,int numone,int limit)这几个参数的意思是:

k:当前处理的是第几位

hasone:之前是否出现过1

numone:之前出现的数字1的数量,可以为负数,所以off函数就是将这个数映射到整数区间。

limit:是否有上界。

其中dp数组的第三维分为是否之前出现过1,这个不要忘记。比如dfs(2,0,0,0)和dfs(2,1,0,0)这两个返回值应该是不一样的。前者之前没有出现过1,所以不能有前导零,合理的只有0和10.而后者合理的有三个:0,01,10

#include 
#include 
#include 
#include 
using namespace std;
int n,m;
int d[35],len;
int dp[32][64][2];
int off(int x){
    return x+32;
}
int dfs(int k,int hasone,int numone,int limit){
    if(k==0)
        return numone<=0;
    if(!limit && dp[k][off(numone)][hasone]!=-1)
        return dp[k][off(numone)][hasone];
    int res = 0;
    if(limit){
        if(d[k]==0)
            res += dfs(k-1,hasone,numone-hasone,1);
        else
            res += dfs(k-1,hasone, numone-hasone, 0) + dfs(k-1,1,numone+1,1);
    }else
        res += dfs(k-1, hasone, numone-hasone, 0) + dfs(k-1, 1, numone+1, 0);
    if(limit == 0)
        dp[k][off(numone)][hasone] = res;
    return res;
}
int solve(int x){
    len = 0;
    while(x){
        d[++len] = x&1;
        x>>=1;
    }
    return dfs(len,0,0,1);
}
int main(){
    memset(dp,-1,sizeof(dp));
    while(scanf("%d %d",&n,&m) != EOF)
        printf("%d\n",solve(m)-solve(n-1));
    return 0;
}


你可能感兴趣的:(poj 3252 数位dp(dfs写法) 二进制0出现比1多的数字个数)