数位dp专题

前言

本篇博客目的是给自己总结复习用,而不是给萌新学习用滴。萌新能不能看得懂需要看造化=.=,毕竟数位 d p dp dp 还是有点难理解的。

概括

数位 d p dp dp,是用来解决一些带有数位上的限制的计数问题的。常见的有问区间 [ L , R ] [L, R] [L,R] 中满足某种数位限制的数的个数,或者求数对的对数等等,也可以用来求和。一般是用 [ 0 , R ] [0,R] [0,R] 的 值减去 [ 0 , L − 1 ] [0, L- 1] [0,L1] 的值。
常常设 d p [ p o s ] [ s t a ] dp[pos][sta] dp[pos][sta] 表示从高到低来到了第 p o s pos pos 位,前面的状态为 s t a sta sta 的数的个数。
d f s dfs dfs 过程中,会有一个参数 l i m i t limit limit,表示当前数位的枚举范围是不是有限制。
只有当前面的每个数位都取到最大值时, l i m i t limit limit 才会等于 1 1 1
l i m i t limit limit 等于 0 0 0 的时候,说明从这一位往后,每个数位都可以取 [ 0 , 9 ] [0, 9] [0,9],这里产生了大量重复或等价的状态,通过记忆化来压缩起来。
数位 d p dp dp 的时间复杂度为 O ( 状 态 数 ∗ 转 移 复 杂 度 ) O(状态数 * 转移复杂度) O()

数位 d p dp dp 的模板

数位 d p dp dp d p dp dp 数组可以分为记录 l i m i t limit limit 和不记录的,当统计的是数对的时候,记录 l i m i t limit limit 会使复杂度降低。

状态中带有 l i m i t limit limit

这种 d p dp dp 数组每次都要进行 m e m s e t memset memset,因为每个数的数位限制不同。

在这里插入图片描述

状态中不带 l i m i t limit limit

这种 d p dp dp 数组可以适用于多组数据,因为状态是普遍的,不是针对单独一个限制的。

在这里插入图片描述

入门题 [hdu2089]不要62

[ L , R ] [L,R] [L,R] 中数字中不带 4 4 4 62 62 62 的数的个数。
d f s dfs dfs 的过程中,传入几个参数:当前到了第几位,前面一位是不是 6 6 6,当前数位有没有限制。

#include <bits/stdc++.h>
using namespace std;

int dp[10][2], a[10];

int dfs(int pos, int f1, int f2){
   
    if(!pos) return 1;
    if(!f2 && dp[pos][f1] != -1) return dp[pos][f1];
    int res = 0, up = f2? a[pos]: 9;
    for(int i = 0; i <= up; i++){
   
        if(i == 4 || (f1 && i == 2)) continue;
        res += dfs(pos - 1, i == 6, f2 && i == up);
    } 
    if(!f2) dp[pos][f1] = res;
    return res;
}

int f(int x){
   
    int tot = 0;
    while(x) a[++tot] = x % 10, x /= 10;
    return dfs(tot, 0, 1);
}
int main(){
   
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    memset(dp, -1, sizeof(dp));
    int l, r;
    while(cin >> l >> r){
   
        if(!l && !r) break;
        cout << f(r) - f(l - 1) << '\n';
    }
    return 0; 
}
[hdu3555] bomb

[ L , R ] [L,R] [L,R] 中包含 49 49 49 的数的个数。
f 1 f_1 f1 表示上一位是不是 4 4 4, f 2 f_2 f2 表示枚举是不是有限制, f 3 f_3 f3 表示前面是否已经出现 49 49 49 了。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

void debug_out(){
   
    cerr << endl;
}
template<typename Head, typename
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值