数位dp小结(状压的第一次试水)

最近刷了好几套数位dp的题目了,感觉对数位dp总算是有了一个清楚的认识了,现在就写个小结

数位dp其实就是对深搜的一个优化

保存一些深搜的状态,那么下次搜索到这个状态的时候就直接返回dp数组中保存的只即可

下面我们来看一道题

XHXJ's LIS

 

题意就是我们给一个范围 l,r要求l,r内,所有lis长度为k的数字的个数

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long


ll K,a[100],dp[25][1<<10][12];


using namespace std;


ll trans(ll data){
    ll n(0);
    while (data){
        n++;
        a[n]=data%10;
        data/=10;
    }
    return n;
}


ll cal_sta(ll data){//找cal里有多少个1
    ll n(0);
    while(data){
        n+=data%2;
        data/=2;
    }
    return n;
}


ll update(ll sta,ll k){//根据现在的状态和k来更新状态
    ll save;
    if ((sta==0)&&(k==0))//对前导0的处理
        return 0;


    for (save=k;save<=10;save++)
        if (sta&(1<<save))
        return (sta-(1<<save))+(1<<k);


    return sta+(1<<k);
}


ll DFS(ll poi,ll sta,ll limit){//poi是第poi位,sta是之前一位的状态哦,limit是有没有上界限制
    if (poi==0)//这个就是一般的dfs,如果搜索到最后就返回就好了
        return cal_sta(sta)==K;


    if (!limit&&dp[poi][sta][K]!=-1)//如果这个状态之前被记录过了,就返回dp的值
        return dp[poi][sta][K];


    int up;//找到上界
    if (limit) up=a[poi];
    else up=9;


    int nsta;
    ll ans(0);
    for (int k=0;k<=up;k++){
        nsta=update(sta,k);//更新状态继续dfs
        ans+=DFS(poi-1,nsta,limit&&k==a[poi]);
    }


    if (!limit)//更新dp的值
        dp[poi][sta][K]=ans;
    return ans;
}


ll cal(ll data){
    int n=trans(data);
    return DFS(n,(ll)0,1);
}


int main(){
    ll t;
    scanf("%lld",&t);


    memset(dp,-1,sizeof(dp));
    for (ll Case=1;Case<=t;Case++){
        ll l,r;
        scanf("%lld %lld %lld",&l,&r,&K);
        printf("Case #%lld: %lld\n",Case,cal(r)-cal(l-1));
    }
    return 0;
}



//最后写一遍这个天杀的数位dp

/*传统的DFS做法就是递归找到最后一位,然后算他的LIS
那么什么状态下他的值就不会改变了呢
在第i位时ans数组一模一样的时候状态就是一样的
ans数组最多有十位嘛而且是严格递增的,感觉跟在此刻填进去的数字是多少没关系呀
二维就够了吧,其实三维有个好处,就是能搞一个k表示要求的长度,这样在处理t组数据的时候就能直接用啦
恩,接下来看看状态压缩dp怎么搞再来写这道题

晚上应该能搞出来
第一次状压一下*/

注意啦,一定要保证状态没有后效性哦,数位dp的关键就是找状态

很多状态的出错都跟前导0有关,所以一般都在dfs里面加一个bool表示有无前导0

这个题目的前导0我用三个参数就特殊处理掉了

所以就没加bool,找状态的时候一定要注意前导0哦

然后也没什么说的了,套路就是这样,

然后我发现通过加减来状压居然比位运算要快些,这个就很迷了


还有就是开始一部分开的是int,一部分开的是ll就wa了

然后全部开ll就A了,这个可能是有些inthe


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值