poj 3252 数位dp(Round Number)

题意:输入两个十进制正整数a和b,求闭区间 [a ,b] 内有多少个Round number。所谓的Round Number就是把一个十进制数转换为一个无符号二进制数,若该二进制数中0的个数大于等于1的个数,则它就是一个Round Number。

思路:用dp来做:dp[i][j]表示二进制长度为 i 的数字含 j 个0的个数,比如dp[3][1] = 2(101和110)。做法就是先打表求出dp数组。然后求出a和b各有多少位,之后特判两边,中间直接求。拿样例来说:问10和1100之间的个数。先求出10~11之间的个数,再求出1000~1100之间的个数,然后求出长度为3的总个数。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define N 36
int dp[N][N],sum[N];
int a,b;
void init(){
    int i,j;
    dp[1][0] = 1;
    clc(sum, 0);
    for(i = 2;i<=31;i++){
        dp[i][0] = 1;
        for(j = 1;j<i;j++){
            dp[i][j] = dp[i-1][j-1]+dp[i-1][j];
            if(j>=(i+1)/2)
                sum[i] += dp[i][j];
        }
    }
}
int solve(int x,int d){
    int i,j,num=0,res=0;
    res = sum[d];
    num = (d+1)/2;
    for(i = d-1;i>0;i--){
        if(0 == ((1<<(i-1))&x)){
            for(j = num;j<i;j++)
                res -= dp[i][j];
            num--;
        }
    }
    return res;
}
int main(){
    int i,j,k,res=0;
    init();
    scanf("%d %d",&a,&b);
    for(i = 30;i>=0;i--)
        if((1<<i)&a)
            break;
    if((a&(a-1))==0)
        res +=sum[i+1];
    else
        res += sum[i+1]-solve(a-1,i+1);
    for(j = 30;j>=0;j--)
        if((1<<j)&b)
            break;
    for(k = i+2;k<=j;k++)
        res += sum[k];
    res += solve(b,j+1);
    if(i == j)
        res -= sum[i+1];
    printf("%d\n",res);
}


你可能感兴趣的:(poj 3252 数位dp(Round Number))