AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

AcWing 1081. 度的数量【数位DP基本概念+数位DP记忆化搜索】    原题链接    中等

作者: 作者的头像   一只野生彩色铅笔 ,  2021-09-14 00:06:18 ,  所有人可见 ,  阅读 6809


105


50

最近在补全提高课所有题目的题解,宣传一下汇总的地方提高课题解补全计划

参考文献:
  1. OiWiki - 数位DP ( 2021/8/19 12:33:29 更新的最新篇章,之前该板块几乎为空,今天找资料的时候发现居然更新了)
  2. 数位DP笔记(DFS做法) - 风总 (写的很棒,非常推荐,前人栽树,我来 洗稿 乘凉~)

数位DP基本概念

数位:把一个数字按照个、十、百、千等等一位一位地拆开,关注它每一位上的数字。

如果拆的是 十进制数,那么每一位数字都是 $0$ ~ $9$,其他进制可 类比 十进制。

数位 DP:用来解决一类特定问题,这种问题比较好辨认,一般具有这几个特征:

  • 要求统计满足一定条件的数的数量(即,最终目的为计数)

  • 这些条件经过转化后可以使用「数位」的思想去理解和判断

  • 输入会提供一个数字区间(有时也只提供上界)来作为统计的限制

  • 上界很大(比如 $10^9$),暴力枚举验证会超时

从题目中分析出 数位DP 的模型后,做法就非常的 套路化 了

题目要求 一段区间 内符合某些条件的数的个数,我们用 前缀和思想 转换为求两个 前缀区间 的问题

$$ res[l,r] = res[1,r] - res[1,l-1] $$

接下来的问题就是统计答案。

统计答案可以选择 记忆化搜索,也可以选择 循环迭代递推。

为了不重不漏地统计所有不超过上限的答案,要 从高到低 枚举每一位

再考虑每一位都可以 填哪些数字,最后利用上述 前缀和思想 统计答案

记忆化搜索 中要引入的参数通常有:

  1. 当前枚举到的数位 $pos$ (搜索的深度)
  2. 前一位数(或是前几位数)的情况 $st$ (诸如 前一位是什么、前几位总和是多少、某个数出现了几次 等)
  3. 前几位的数字是否等于上界的前几位数字 $op~(0/1)$(限制本次搜索的数位范围)
  4. 是否有前导零 $lead~(0/1)$

记忆化搜索的 递归搜索树 y总已经画过了,这里就不再额外绘制(本身也不复杂,就留给读者了)

循环迭代递推 的方法,大家可以看 y总 的 算法提高课 以及 lyd 的 蓝书 上的介绍

何时能用 记忆化搜索 优化掉当前搜索分支

当当前 数位 能够枚举集合内所有元素的时候(没有贴着上界),即 !op

题目描述

求给定区间 $[L, R]$ 中满足:这个数恰好等于 $K$ 个互不相等的 $B$ 的整数次幂之和

分析

题目中有明确暗示,是一个 $B$ 进制的 数位问题(每个整数次幂位数的数字选择)

题目中还要求 恰好有 $K$ 个 互不相等 的 $B$ 的整数次幂之和

故对于该 $B$ 进制的数字来说,每一位数要么填 $0$,要么填 $1$,且填 $1$ 的位数个数 恰好等于 $K$

那么本题就是一个 01数位DP问题,接下来就是套模板时间

模板中的 $st$ 在本题里要记录的 参数 就是,前几位中,填 $1$ 的个数

Code(记忆化搜索)

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 35;

int l, r, k, b;
int a[N], al;
int f[N][N];

int dp(int pos, int st, int op) //op: 1=,0<
{
    //枚举到最后一位数位,是否恰有k个不同的1(也是递归的终止条件)
    if (!pos) return st == k;
    //记忆化搜索,前提是不贴着上界(可以枚举满这一位所有的数字)
    if (!op && ~f[pos][st]) return f[pos][st];
    //01数位dp,贴着上界时,本轮能枚举的最大数就是上界数位的数字和1之间的最小值
    int res = 0, maxx = op ? min(a[pos], 1) : 1;
    for (int i = 0; i <= maxx; i ++ )
    {
        if (st + i > k) continue;
        res += dp(pos - 1, st + i, op && i == a[pos]);
    }
    return op ? res : f[pos][st] = res;
}
int calc(int x)
{
    al = 0; memset(f, -1, sizeof f);        //模板的必要初始化步骤
    while (x) a[ ++ al] = x % b, x /= b;  //把x按照进制分解到数组中
    return dp(al, 0, 1);
}
int main()
{
    cin >> l >> r >> k >> b;
    cout << calc(r) - calc(l - 1) << endl;
    return 0;
}

Code(循环迭代递推)

见y总视频,或者蓝书

17 评论


用户头像
simonhan   2022-01-07 14:29      10    踩      回复

我觉得还是提一下记忆化和递推的f数组含义不一样吧,开始没理解就是在这里。记忆化的f[i][j]代表的是不贴上界,当前位置是i,之前的数是j的情况下的合法方案。为什么要不贴上界(很多都没提)?因为当前位置的limit无约束之后才能xjb选树,如果紧贴上界的话,后面的数就不能随便选了。只有不贴上界,这个f[i][j]才是可以复用的。否则这个状态和后面的f[i][j]不具备一致性。

用户头像
一只野生彩色铅笔   2022-01-07 15:16         踩      回复

谢谢指出,已经加上这句引导了 w

用户头像
simonhan   2022-01-07 20:52    回复了 一只野生彩色铅笔 的评论         踩      回复

2333,我也是问了0x3f才弄明白的。谢谢程师傅吧

用户头像
Nan97   2022-02-04 20:59    回复了 一只野生彩色铅笔 的评论         踩      回复

如果数组再加一维 limit 是不是就可以避免这个问题了

用户头像
一只野生彩色铅笔   2022-02-04 21:18    回复了 Nan97 的评论      1    踩      回复

贴着上界的搜索分支有且只有一条主路径,没有优化那一维的必要

但是加上可以不用特判,空间常数够的话可以加

用户头像
Nan97   2022-02-04 22:28    回复了 一只野生彩色铅笔 的评论      1    踩      回复

这个题只有0和1两种选择确实没必要加,如果那种枚举数字范围0-9的话还是可以加的😂 毕竟空间常数只是 2

用户头像
xioachou   2023-05-19 19:40         踩      回复

orz,终于解惑了


用户头像
IShowMeat   2023-12-11 20:24         踩      回复

强啊!推荐A友配合知乎Pecco大佬解析加深理解
https://zhuanlan.zhihu.com/p/348851463


用户头像
Bianca   2023-01-12 20:16         踩      回复

洛谷P4317 花神的数论题, 用y总的模板,最后相乘不知道咋弄,感觉好亏啊,写到了最后,发现就差个乘,大佬有说法吗

用户头像
RwChen   2023-03-21 08:24         踩      回复

数位dp写迭代版本的都是大神,还是记忆化好写一些


用户头像
Fzldq   2022-10-25 14:03         踩      回复

Orz,还是记忆化搜索好写


用户头像
熬夜波比   2022-04-22 15:39         踩      回复

$res[l,r]=res[1,r]−res[1,l−1]$ 这里的区间是$[0,r]$和$[0,l-1]$, 不然的话令$l=1$ 会出现$[1,0]$ 这种情况 另外粉笔大佬写的题解很棒


用户头像
S_66   2021-12-27 21:02         踩      回复

op就两种可能。再开一维就不用特判了。


用户头像
Dear.隆冬   2021-10-11 22:44         踩      回复

为什么一定要视op的情况来选择是否返回$f[pos][st]$,如果让$f[pos][st]$直接代表不论是否填上界的答案,会出现什么问题呢,试过了确实没AC,可有没有大佬能解释为什么啊

用户头像
一只野生彩色铅笔   2021-10-12 08:25         踩      回复

是否贴着上届,决定了枚举的下一位数的范围


用户头像
Dear.隆冬   2021-10-11 21:50         踩      回复

还是记忆化搜索既好看又好写
大佬%%%%%%%%


用户头像
征途者   2021-10-06 22:47         踩      回复

彩铅大佬%%%


App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息