POJ 3252 Round Numbers 数位DP 详解

题意:

给出一个数字区间  问这个区间内有多少个数 满足以下条件

二进制表示下 0 的个数大于或等于 1 的个数

解题方法

数位DP

用 dp[x][n1][n0] 表示当前处在第 x 位 (从高位到低位) 1 的个数为n1 0 的个数为 n0(前导0不算) 且当前位的取值没限制时的合法数字的总数

无限制的意思是 如果要求区间  [0, 123] 之间有多少个合法数字 那么当最高位为 1 时 次高位的取值就只能是 0->2 了 如果为3 就是130+了

记忆化搜索 即可快速求得结果

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
int dp[32][32][32];//位数 1的个数 0的个数(不含前导零
int d[32],c;
int dfs(int x,int n1,int n0,int lim)//位数 1的个数 0的个数 当前位数取值是否受到限制
{
    if(x==0) return n0>=n1?1:0;//如果处理完1~c的位数了就根据 0 的个数和 1 的个数返回正确的值
    if(lim==0 &&dp[x][n1][n0]!=-1) return dp[x][n1][n0]; 记忆化优化
    int ans=0, n=lim?d[x]:1;// n 是当前位能取得到的最大值
    for(int i=0;i<=n;i++)
    if(i==0) ans+=dfs(x-1,n1,n0+(n1!=0)/*处理前导零 保证该状态的数有效*/,lim&&i==n);
    如果当前位取值有限制且这位上的数字又取到了边界值 则下一位有限制
    else ans+=dfs(x-1,n1+1,n0,lim&&i==n);
    if(lim==0) dp[x][n1][n0]=ans;//赋值
    return ans;

}
int solve(int n)
{
     c=0;
    while(n)
    {
        d[++c]=n%2;
       n>>=1; //n/=2;
    }
    return dfs(c,0,0,1);
}
int main()
{
    int n,m;
    memset(dp,-1,sizeof dp); 只需要初始化一次 因为这个 dp数组都是当前位取值无限制下的结果
    while(~scanf("%d%d",&n,&m))
    {
        printf("%d\n",solve(m)-solve(n-1));
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值