数位dp专题

本文主要介绍了数位动态规划(DP)的概念和应用场景,包括数位DP的两种模板:带状态limit和不带状态limit。通过多个题目实例解析,如[hdu2089]不要62、[hdu3555] bomb等,详细阐述了如何使用数位DP解决数位限制的计数、求和等问题,总结了数位DP的解题思路和常见题型。

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

前言

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

概括

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

数位 dpdpdp 的模板

数位 dpdpdpdpdpdp 数组可以分为记录 limitlimitlimit 和不记录的,当统计的是数对的时候,记录 limitlimitlimit 会使复杂度降低。

状态中带有 limitlimitlimit

这种 dpdpdp 数组每次都要进行 memsetmemsetmemset,因为每个数的数位限制不同。

在这里插入图片描述

状态中不带 limitlimitlimit

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

在这里插入图片描述

入门题 [hdu2089]不要62

[L,R][L,R][L,R] 中数字中不带 444626262 的数的个数。
dfsdfsdfs 的过程中,传入几个参数:当前到了第几位,前面一位是不是 666,当前数位有没有限制。

#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] 中包含 494949 的数的个数。
f1f_1f1 表示上一位是不是 444, f2f_2f2 表示枚举是不是有限制,f3f_3f3 表示前面是否已经出现 494949 了。

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

void debug_out(){
   
   
    cerr << endl;
}
template<typename Head, typename... Tail>
void debug_out(Head H, Tail... T){
   
   
    cerr << " " << to_string(H);
    debug_out(T...);
}
#ifdef local
#define debug(...) cerr<<"["<<#__VA_ARGS__<<"]:",debug_out(__VA_ARGS__)
#else
#define debug(...) 55
#endif

LL dp[22][2][2];
int a[22];
LL dfs(int pos, int f1, int f2, int f3){
   
   
	//debug(pos, f1, f2, f3);
	if(!pos) return f3;
	if(!f2 && dp[pos][f1][f3] != 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值