(数位dp)BZOJ1662

本文介绍了一道数位DP题目,通过记忆化搜索解决。详细解释了如何从高位到低位进行搜索,并通过限制条件确保状态的合法性。代码中包含具体的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这道题是一道数位dp的题目,我的方法是记忆化搜索,当然也可以预处理f [ i ] [ j ]表示 i 个空里不超过 j 个1的方案数,得出方程

f [ i ] [ j ] = f [ i - 1] [ j ] + f [ i - 1 ][ j - 1 ]

既然我用的是记忆化搜索,那就不啰嗦 f 了

记忆化搜索的核心就是从最高位开始一直搜到最低位,然后如果此状态合法,就返回1,然后在搜索的时候有两个限制,第一个是前面是否全是0,因为这样从第一个1开始才开始计算0的个数,如果冒然搞会出问题,第二个是前面的决策是否给本决策留了决策的余地,因为如果没留余地就只能照着原位来了假设原数为11000,那么你现在填了10,你下面就可以为所欲为了

具体解释代码中有

#include<iostream>
#include<cstring>
using namespace std;
int l,r;
int len=0;
int f[50][50][50];
int s[50];
int ans=0;
int dfs(int i,int x1,int x2,bool bz,bool flag)//flag表示前导0的限制,bz表示前面是否给后面
//留了余地,也就是说如果有一位原数为1,但你填了0,那么下面你就可以随便填了
{
    //cout<<i<<" ";
    if(!i)
    {//如果到了0位
        //<<"{"<<i<<" "<<x1<<" "<<x2<<" "<<bz<<" "<<flag<<"}";
        if(flag==1) return 1;//如果为前导0状态,即都填0,那么也算一种状态了
        if(x1>=x2) return 1;//如果当前0的位数大于等于1的位数那么就为合法状态返回1
        return 0;//当前状态不合法
    }
    if(!bz && !flag && f[i][x1][x2]!=-1) return f[i][x1][x2];//如果没任何限制并且以前求过
    int last;
    if(bz) last=s[i];//如果前面有限制,那么这就得规规矩矩地来
    else last=1;//如果前面没限制,那么就随便来,因为下面是0到last循环,所以这么写
    int ans=0;
    for(int j=0;j<=last;j++)
    {//枚举本位填1还是填0
        if(flag)//如果当前为前导0状态
        {
            if(!j)/*如果这一位还是0*/ ans+=dfs(i-1,0,0,/*如果为前导0状态那么都是0,而且最后组成的数前导0也不会算进去,也就不用关心1和0的个数了*/bz&&last==j,1);
            else ans+=dfs(i-1,x1,x2+1,bz&&last==j,0);//若本位随便填,那下一位也随便填,如果本位不随便,那么看当前填的是不是根原来相等《因为是从0枚举》,若相等那么下一位得规矩了
        }
        else//如果不是前导0就正常来
        {
            if(!j)ans+=dfs(i-1,x1+1,x2,bz&&last==j,0);
            else ans+=dfs(i-1,x1,x2+1,bz&&last==j,0);
        }
    }
    if(!flag && !bz) f[i][x1][x2]=ans;//如果什么限制都没有那么可以随意用,存起来给以后用
    return ans;
}
int get(int n)
{
    len=0;
    while(n)
    {
        s[++len]=n%2;
        n/=2;
    }
    ans=dfs(len,0,0,1,1);
    return ans;
}
int main()
{
    cin>>l>>r;
    memset(f,-1,sizeof(f));
    cout<<get(r)-get(l-1);
}
//从最高位开始往后搜,搜到最后一位如果发现是合法状态就返回1

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值