题目链接:http://poj.org/problem?id=3252
题目大意:给你两个数L,R,求[L, R]区间内,二进制下0的个数>1的个数的数字个数。

这里和之前的题不同的是:有前导0的影响。所以必须满足没有前导0和没有上界限制才能记忆化递归。

#include <bits/stdc++.h>

#define LL long long
using namespace std;
int a[35];
int dp[35][35][35];
int dfs(int pos, int s1, int s2, int Lead, int mt)//s1:0的个数, s2:1的个数 Lead:前导0 mt:限制
{
    if(pos==-1)
    {
        if(s1>=s2)//0的个数>1的个数
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    if(!mt&&!Lead&&dp[pos][s1][s2]!=-1)//必须满足两个条件
    {
        return dp[pos][s1][s2];
    }

    int Len=mt?a[pos]:1;
    int ans=0;
    for(int i=0;i<=Len;i++)
    {
        if(i==0)
        {
            if(Lead)
            {
                ans+=dfs(pos-1, s1, s2, Lead, mt&&i==a[pos]);//有前导0时,这位取0,s1, s2不变化
            }
            else
            {       
                ans+=dfs(pos-1, s1+1, s2, Lead, mt&&i==a[pos]);//没有前导0时,这位取0,s1+1
            }
        }
        else
        {
            ans+=dfs(pos-1, s1, s2+1, false, mt&&i==a[pos]);//取1时, s2+1
        }
    }

    if(!Lead&&!mt)
    {
        dp[pos][s1][s2]=ans;
    }

    return ans;

}

int slove(int x)
{
    int pos=0;
    while(x)
    {
        a[pos++]=x%2;
        x/=2;
    }

    return dfs(pos-1, 0, 0, true, true);
}

int main()
{
    memset(dp, -1, sizeof(dp));
    int L, R;
    scanf("%d%d",&L,&R);
    printf("%d\n", slove(R)-slove(L-1));

    return 0;
}