POJ-3252 Round Numbers (数位DP)

Round Numbers
http://poj.org/problem?id=3252
Time Limit: 2000MS   Memory Limit: 65536K
     

Description

The cows, as you know, have no fingers or thumbs and thus are unable to play Scissors, Paper, Stone' (also known as 'Rock, Paper, Scissors', 'Ro, Sham, Bo', and a host of other names) in order to make arbitrary decisions such as who gets to be milked first. They can't even flip a coin because it's so hard to toss using hooves.

They have thus resorted to "round number" matching. The first cow picks an integer less than two billion. The second cow does the same. If the numbers are both "round numbers", the first cow wins,
otherwise the second cow wins.

A positive integer N is said to be a "round number" if the binary representation of N has as many or more zeroes than it has ones. For example, the integer 9, when written in binary form, is 1001. 1001 has two zeroes and two ones; thus, 9 is a round number. The integer 26 is 11010 in binary; since it has two zeroes and three ones, it is not a round number.

Obviously, it takes cows a while to convert numbers to binary, so the winner takes a while to determine. Bessie wants to cheat and thinks she can do that if she knows how many "round numbers" are in a given range.

Help her by writing a program that tells how many round numbers appear in the inclusive range given by the input (1 ≤ Start < Finish ≤ 2,000,000,000).

Input

Line 1: Two space-separated integers, respectively  Start and  Finish.

Output

Line 1: A single integer that is the count of round numbers in the inclusive range  Start.. Finish

Sample Input

2 12

Sample Output

6

题目大意:统计区间[sta,des]中,二进制表示下0的个数比1的个数多的数字总数(不含前导0)?


写完后看了很多题解,发现大家用数位DP都要用dfs,感觉直接循环也很好理解啊...

设dp[i][j][0]表示长度为i,含有j个0且最高位为0的数字的个数;dp[i][j][1]表示长度为i,含有j个0且最高位为1的数字的个数

初始:dp[1][0][1]=1;//长度为1,含有0个0且最高位为1的数字个数为1
          dp[1][1][0]=1;//长度为1,含有1个0且最高位为0的数字个数为1

然后通过状态转移,预处理出所有的结果

最后再在函数中进行从高位到低位的按位统计

差不多有点感觉了,貌似数位DP都是先进性预处理,再按位统计即可


#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

int num[35],len,sta,des,ans,cnt[2];//cnt[i]表示该数二进制中从高位到当前位i出现的次数(i=0,1)
int dp[35][35][2];//dp[i][j][0]表示长度为i,含有j个0且最高位为0的数字的个数;dp[i][j][1]表示长度为i,含有j个0且最高位为1的数字的个数

void Init() {
    memset(dp,0,sizeof(dp));
    dp[1][0][1]=1;//长度为1,含有0个0且最高位为1的数字个数为1
    dp[1][1][0]=1;//长度为1,含有1个0且最高位为0的数字个数为1
    for(int i=2;i<=31;++i) {
        dp[i][0][1]=1;//长度为i,含有0个0且最高位为1的数字个数为1
        for(int j=1;j<=i;++j) {
            dp[i][j][0]=dp[i-1][j-1][0]+dp[i-1][j-1][1];//第i位取0,后i-1位中含有j-1个0,第i-1位既可取0,也可取1
            dp[i][j][1]=dp[i-1][j][0]+dp[i-1][j][1];//第i位取1,后i-1位中含有j个0,第i-1位既可取0,也可取1
        }
    }
}

int getCnt(int x) {//返回区间[1,x]中二进制表示下,0个数大于等于1个数的数字总数
    cnt[0]=cnt[1]=ans=len=0;
    while(x>0) {
        num[++len]=x%2;
        x/=2;
    }
    for(int i=len;i>1;--i) {
        for(int j=((i-1)>>1)+((i-1)&1);j<i;++j) {//从i起的高位都取0,第i-1位取1,后i-1位中0个数大于等于1个数的数字个数
            ans+=dp[i-1][j][1];
        }
        if(num[i]==1) {
            if(cnt[1]!=0) {
                for(int j=max(0,(len>>1)+(len&1)-1-cnt[0]);j<i;++j) {//从i+1起的高位与原数一样,第i位取0,后i-1位中0个数+已出现的0个数大于等于1个数+已出现的1个数
                    ans+=dp[i-1][j][0]+dp[i-1][j][1];
                }
            }
            ++cnt[1];
        }
        else {//当前位为0
            ++cnt[0];
        }
    }
    if(num[1]==1) {//如果最低位为1
        if(cnt[1]!=0) {//高位出现过1(即该数不是1)
            if(cnt[0]>cnt[1]) {//高位中0出现的次数多于1出现的次数,则当前位为0,1时均合法
                ans+=2;
            }
            else if(cnt[0]==cnt[1]) {//高位中0出现的次数等于1出现的次数,则当前位取0时合法
                ++ans;
            }
        }
    }
    else if(len>0) {//如果该数不为0,且当前位为0
        ++cnt[0];
        if(cnt[0]>=cnt[1]) {//该数中所有的0的个数大于1的个数,则此数合法
            ++ans;
        }
    }
    return ans;
}

int main() {
    Init();
    while(2==scanf("%d%d",&sta,&des)) {
        printf("%d\n",getCnt(des)-getCnt(sta-1));
    }
    return 0;
}


你可能感兴趣的:(poj,数位dp)