POJ Round Numbers(数位DP)

题目大意:

Round Number:  将一个整数转化为二进制数字后,(不含前导0) 要是0的个数 大于等于1的个数 则是 Round Number

问从L-R之中有多少个Round Number

题目分析:

要转化为2进制数字,我们用10进制保存明显不好判断0和1的个数,所以选择用8进制来存储,这样的话每一次进位会多出 ”000“, 然后再加上8进制的尾数, 最后我们可以得出增加了几个 1 和 增加了 几个 0

需要注意的一点就是, 当前面的数字小于 8 的时候 我们要对 前导 0 进行特殊判断

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef __int64 LL;
LL dp[20][163][133];//dp[位数][0的个数][1的个数]
int bit[50];
int binary[8][2] = { {3,0}, {2,1},{2,1},{1,2},{2,1},{1,2},{1,2},{0,3} };
LL dfs(int pos,int preCou0,int preCou1,int flag,int len)
{
    if(pos == -1)
    {
        return  (preCou1 || preCou0) && preCou0 >= preCou1;//这里要对 0 进行特殊判断, 将0去掉
    }

    if( !flag && dp[pos][preCou0][preCou1] != -1)
        return dp[pos][preCou0][preCou1];

    LL ans = 0;
    int end = flag?bit[pos]:7;

    for(int i=0; i<= end; i++)
    {
        int nowCou0 = preCou0 + binary[i][0];
        int nowCou1 = preCou1 + binary[i][1];
        if(preCou0 == 0 && preCou1 == 0)//判断是否是第一位,若是第一位则需要进行特殊处理
        {
            if(i == 0 || i == 1)nowCou0 = 0;
            if(i == 2)nowCou0 = 1;
            if(i == 3)nowCou0 = 0;
        }

        ans += dfs(pos-1, nowCou0, nowCou1, flag && i == end, len);
    }
    if(!flag)
        dp[pos][preCou0][preCou1] = ans;

    return ans;
}


LL solve(LL n)
{
    int len = 0;

    while(n)
    {
        bit[len++] = n%8;
        n /= 8;
    }
    
    return dfs(len-1, 0, 0, 1, len-1);
}

int main()
{
    LL a, b;
    
    memset(dp, -1 ,sizeof(dp));
    while(scanf("%I64d%I64d", &a, &b) != EOF)
    {
    //    printf("%I64d\n", solve(a));
    //    printf("%I64d\n", solve(b));
        printf("%I64d\n", solve(b) - solve(a-1) );
    }
    return 0;
}

/*

0的个数 大于等于 1的个数
1    0001
2    0010   1
3    0011
4    0100   1
5    0101
6    0110 
7    0111
8    1000   1
9    1001   1
10   1010   1
11   1011
12   1100   1
13   1101   
14   1110
15   1111
16   10000  1

*/

 

posted @ 2015-04-25 09:13  向前走丶不回首  阅读(275)  评论(0编辑  收藏  举报