poj3252(记忆优化,数位dp)

题意:

找出给定范围中满足这个条件的数的个数:这个数各个转化成2进制0个数大于1的个数。

题解:
设定状态:因为这题各个数位的关系只与0、1的个数有关,那么就可以这样dp[pos][one][zero]位数pos一的个数为one零的个数为zero满足条件数的个数。

注意前导零的问题,所以在搜索时加了特别的判断!因为二进制只有0和1,前导零是不算在这个数中零的个数上面的。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<math.h>
using namespace std;
typedef __int64 lld;
#define oo 0x3f3f3f3f
#define digit 66
#define maxn 82
lld dp[digit][digit][digit];
int bit[digit];

lld dfs(int pos,int one,int zero,int pre0,int f)
{
    if(pos<1) return zero>=one;
    if(!f&&dp[pos][one][zero]!=-1) return dp[pos][one][zero];
    int last=f?bit[pos]:1;
    lld res=0;
    for(int i=0;i<=last;i++)
    {
        //pre0=0表示有前导零,pre0=1表示没有
        if(pre0==0)
        {
            if(i==0)
                res+=dfs(pos-1,0,0,0,f&&i==last);
            else
                res+=dfs(pos-1,1,0,1,f&&i==last);
        }
        else
        {
            if(i==0)
                res+=dfs(pos-1,one,zero+1,pre0,f&&i==last);
            else
                res+=dfs(pos-1,one+1,zero,pre0,f&&i==last);
        }
    }
    if(!f) dp[pos][one][zero]=res;
    return res;
}

lld Cnt(lld n)
{
    int len=0;
    while(n)
    {
        bit[++len]=n%2;
        n/=2;
    }
    return dfs(len,0,0,0,1);
}

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


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