POJ 3252Round Numbers(数位DP)

POJ 3252Round Numbers

题目大意

如果一个数的二进制中0的数目大于等于1那么就称这个数为Round Number,问你区间[L,R]中有多少Round Number, (1LR2109)

分析

问题可以转化成问从1到n有多少个Round Number

用f[i]表示 2i 之前的Round Number数目

dp[i][j] 表示长为i,0和1的数目差为j的情况数。

接下来就是状态的转移了,状态的转移稍微麻烦一点,

比如1010
然后1000~1001
最后1010~1010
大概就是这个思路,具体见代码。

一开始交了几发WA后手动测试几组小数据都对了,看代码后发现有些int应该改成long long int,改后又WA,查了一会差不出,到网上找了份AC代码对拍发现131072后的数据就不对了,这么小不会是int的问题,最后才发现是dp[i][j]数组中j开小了。

这道题折腾了我快一天了,心累。

我的方法不太优美,可以参考这篇POJ3252:Round Numbers(数位DP+记忆化DFS)

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
using namespace std;
#define  LL long long int
LL Start,Finish;
LL dp[35][70];//dp[i][j]表示长为i,0和1的数目差为j的情况数,j为31时表示01差的数目为0
LL f[35];//f[i]表示2^i之前(不包含2^i)的Round Number数目
void Dp()
{
      dp[1][32]=1;//边界
      dp[1][30]=1;
      for(int i=2;i<=31;i++)//总长为i的情况
            for(int k=1;k<=i-1;k++)//长为k的情况
                  for(int j=31-i;j<=31+i;j++)//0和1差的数目
                        dp[i][i-k-1-1+j]+=dp[k][j];
}
void Get_f()
{
      f[0]=1;//边界
      LL temp_ans=0;
      for(int i=1;i<=31;i++)
      {
            for(int j=31;j<=31+i;j++)
                  temp_ans+=dp[i][j];
            f[i]=temp_ans;
      }
 }
void To2(LL n,int b[],int &lenth)//将n转换成二进制存到b数组中,下标从0开始,lenth是最终二进制长度
{
      lenth=0;
      while(n)
      {
            b[lenth++]=n%2;
            n=n/2;
      }
}
LL Solve(LL n)//求从0到n有多少Round number
{
    if(n<=1)return 1;
    LL ans=0;
    int b[35];
    int lenth;
    To2(n,b,lenth);
    int diff=-1;//到目前为止的01差
    ans=f[lenth-1];
    for(int loc=lenth-2;loc>=0;loc--)
    {
         if(loc==0)//边界
         {
              if(b[loc]==1 )
              {
                   if(diff-1>=0)ans++;
                   if(diff+1>=0)ans++;
              }
              else if(b[loc]==0 && diff+1>=0)ans++;
              return ans;
         }
         if(b[loc]==1)
         {
                for(int i=1;i<=loc;i++)
                    for(int j=31-i;j<=31+i;j++)
                        if(diff+j+loc+1-i>=31)
                            ans+=dp[i][j];
                 diff--;
         }
         else diff++;
    }
    return ans;
}
int main()
{
    //freopen("data.in","r",stdin);
    //freopen("data1.out","w",stdout);
    LL ans;
    Dp();
    Get_f();
    while(scanf("%lld%lld",&Start,&Finish)!=EOF)
    {
         ans=Solve(Finish)-Solve(Start-1);
         printf("%lld\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值