我看了半天的数位 DP,DP 没学会,人倒是麻了。

解决什么

一般用于求解给你一个区间 \([l,r]\),问你其中满足条件的数有多少个。

这种题目还是蛮常见的,我们一般情况下暴力只能拿一少部分分,之前我看着那个 \(n\le 10^{18}\) 是一脸懵逼,这东西 \(O(n)\) 都过不去,啥高级的东西能 A 啊。

然后就有了今天让我麻了的数位 DP。

思想

题目中给的让我们难以下手,我们不如转化一下:求 \([1,r]\) 中符合限制的数并减去 \([1,l-1]\) 的数。

这样就好处理多了,当然也可以从 \(0\) 开始,根据题目而定。

然后我们把要求的 \([1,x]\) 区间中的 \(x\) 给一位一位分解开,然后 dfs 往里面填数。

在分解的时候,我们用一个数组 \(a[i]\) 来存储从高位到低位(一般是)的数字,来当作填数的限制。

我们在 dfs 的时候,传的参数至少是包含 pos 当前填到第几个数以及 limit 也就是当前点是否有限制,如果有的话,我们在后面遍历当前点填的数的时候直接调用之前的 \(a[]\) 数组就好了。

当然我们在 dfs 的时候是要记忆化的,不然复杂度直接飙升,我们可以根据题目给的限制条件来把状态相同的归到一类然后存放到数组里面,然后我们就可以在遇到与当前状态相同的时候直接调用记忆化数组来让我们的复杂度变得美丽。

遍历每一个数的时候一般分为两种情况,一个有前导零,一个没有前导零。

P2602 [ZJOI2010] 数字计数

code:

#include <bits/stdc++.h>

#define int long long
#define N 20 using namespace std; int a[N], cnt, f[N][N << 3][2][2], dight; inline int dfs(int p, int cntd, int lead, int limit)//p是当前位置,cntd是当前答案lead是有没有前导零。limit是当前数字枚举到的数量上限
{
if(p == cnt) return cntd;//到了就直接返回搜到的值
if(f[p][cntd][lead][limit] != -1) return f[p][cntd][lead][limit];//记忆化,以前搜过了就直接返回
int ans = 0;//统计答案
for(int v = 0; v <= (limit ? a[p] : 9); v ++)//枚举当前点可以是哪些数字
{
if(lead && v == 0)//如果要是当前点有前导零,并且当前的点的下一个枚举的是0
ans += dfs(p + 1, cntd, 1, limit && v == a[p]);//答案累加,计算当前状态下的答案标记有前导零
else
ans += dfs(p + 1, cntd + (v == dight), 0, limit && v == a[p]);//正常情况
}
return f[p][cntd][lead][limit] = ans;//返回答案的同时记忆化
} inline int fx(int x)
{
cnt = 0;
memset(f, -1 , sizeof f);
memset(a, 0, sizeof a);//清空数组
while(x) a[cnt ++] = x % 10, x /= 10;//由低位到高位
reverse(a, a + cnt);//反转一下让他顺序变正常
return dfs(0, 0, 1, 1);//开始搜索 前面有0并且第一个数是有限制的
} signed main()
{
int L, R;
cin >> L >> R; for(int i = 0; i <= 9; i ++)//枚举九个数字
{
dight = i;//更新dight的值
cout << fx(R) - fx(L - 1) << " ";//跑一遍输出当前数字出现的次数
} return 0;
}

和前面讲的一样,利用记忆化搜索,注释应该很清楚了吧。

P8764 [蓝桥杯 2021 国 BC] 二进制问题

数位 DP 板子题。

我们设 \(f_{i,j}\) 为当前从左往右枚举到第 \(i\) 个数没有枚举时,当前枚举完的 \(1\) 的个数为 \(j\) 时的能得到的有 \(k\) 个 \(1\) 的个数。

我们用 ? 来表示当前点没有填入,假设我们现在从左往右填,当前的状态是 10101?????,我们 dfs 完以后,直接存入 \(f_{6,3}\) 里,我们要是再枚举到类似 10011????? 这种的,我们可以发现,后面问号的可能性是一样的,也就是说,他们得到的答案是一样的,那么我们就可以进行记忆化了。

我们对于给定的 \(n\) 按照其他的数位 DP 一样拆成二进制下的数,将每一位都存放到 \(a_{i}\) 里,也就是说 \(a_{i}\) 表示从左往右第 \(i\) 个数可以填 \(1\sim a_{i}\)。

由于这里的情况很少,只有 \(0\) 和 \(1\),所以可以直接展开循环。

code:

#include <bits/stdc++.h>

#define int long long
#define N 100 using namespace std; int n, k, a[N], f[N][N];//枚举到第i个数当前当前j个1的个数 inline int dfs(int p, int limit, int cnt)
{
if( cnt > k ) return 0;
if(! p) return (cnt == k ? 1 : 0);
if(! limit && f[p][cnt] != -1) return f[p][cnt];
int res = 0, flag = (limit ? a[p] : 1);
res += dfs(p - 1, limit && flag == 0, cnt);
if(flag) res += dfs(p - 1, limit && flag == 1, cnt + 1);
if (! limit) f[p][cnt] = res;
return res;
} inline int fx(int x)
{
memset(f, -1, sizeof f);
int len = 0;
while(x) a[++ len] = (x & 1), x >>= 1;
return dfs(len, 1, 0);
} signed main()
{
cin >> n >> k;
cout << fx(n) << endl;
return 0;
}

数位DP?记忆化罢了!的更多相关文章

  1. 数位dp/记忆化搜索

    一.引例 #1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an  ...

  2. [hihocoder 1033]交错和 数位dp/记忆化搜索

    #1033 : 交错和 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描写叙述 给定一个数 x,设它十进制展从高位到低位上的数位依次是 a0, a1, ..., an - 1 ...

  3. 【poj1850】 Code 数位dp+记忆化搜索

    题目大意:给你一个字符串,问你这个字符串的rank,如果这个字符串不合法,请直接输出0.(一个合法的字符串是对于∀i,有c[i]<c[i+1]) 字符串s的rank的计算方式:以字符串长度作为第 ...

  4. 【poj3252】 Round Numbers (数位DP+记忆化DFS)

    题目大意:给你一个区间$[l,r]$,求在该区间内有多少整数在二进制下$0$的数量$≥1$的数量.数据范围$1≤l,r≤2*10^{9}$. 第一次用记忆化dfs写数位dp,感觉神清气爽~(原谅我这个 ...

  5. [BZOJ3598][SCOI2014]方伯伯的商场之旅(数位DP,记忆化搜索)

    3598: [Scoi2014]方伯伯的商场之旅 Time Limit: 30 Sec  Memory Limit: 64 MBSubmit: 449  Solved: 254[Submit][Sta ...

  6. bzoj1833: [ZJOI2010]count 数字计数(数位DP+记忆化搜索)

    1833: [ZJOI2010]count 数字计数 题目:传送门 题解: 今天是躲不开各种恶心DP了??? %爆靖大佬啊!!! 据说是数位DP裸题...emmm学吧学吧 感觉记忆化搜索特别强: 定义 ...

  7. 1026-windy数+数位DP+记忆化搜索

    1026: [SCOI2009]windy数 题意:数位DP模板题: 目前只理解了记忆化搜索,就想练练手, ------给递推写法留一个位子 ------ 注意这道题要判断前导0的情况,1 )可以加一 ...

  8. luogu P2657 [SCOI2009]windy数 数位dp 记忆化搜索

    题目链接 luogu P2657 [SCOI2009]windy数 题解 我有了一种所有数位dp都能用记忆话搜索水的错觉 代码 #include<cstdio> #include<a ...

  9. 【每日dp】 Gym - 101889E Enigma 数位dp 记忆化搜索

    题意:给你一个长度为1000的串以及一个数n 让你将串中的‘?’填上数字 使得该串是n的倍数而且最小(没有前导零) 题解:dp,令dp[len][mod]为是否出现过 填到第len位,余数为mod 的 ...

  10. hdu3652 数位dp记忆化搜索

    从未见过的船新版本数位dp,,省去了预处理过程,直接进行计算 #include<bits/stdc++.h> using namespace std; #define ll long lo ...

随机推荐

  1. R语言网络数据爬取

    现在大家对爬虫的兴趣不断高涨,R和PYTHON是两个非常有力的爬虫工具.Python倾向于做大型爬虫,与R相比,语法相对复杂,因此Python爬虫的学习曲线会相对陡峭.对于那些时间宝贵,又想从网上获取 ...

  2. pysimplegui之运行多个窗口

    运行多个窗口 这就是 PySimpleGUI 继续简单的地方,但问题空间刚刚进入"复杂"领域. 如果您希望在事件循环中运行多个窗口,那么有两种方法可以做到这一点. 当第二个窗口可见 ...

  3. SpringCloud源码学习笔记3——Nacos服务注册源码分析

    系列文章目录和关于我 一丶基本概念&Nacos架构 1.为什么需要注册中心 实现服务治理.服务动态扩容,以及调用时能有负载均衡的效果. 如果我们将服务提供方的ip地址配置在服务消费方的配置文件 ...

  4. 脚本:auto_send_tablespace定期发送表空间巡检到邮箱

    简述:周期定时发送表空间到指定邮箱内 1.修改邮箱配置 /etc/mail.rc,具体细节见网上教程 $ vi /etc/mail.rc set from=123456@qq.comset smtp= ...

  5. Linux(六)进程管理

    Linux系统管理 linux中的进程与服务 进程:Linux中正在执行的程序或者命令 服务:Linux中一直存在.常驻内存的进程 守护进程:进程按照运行方式进行划分,又分为前台显示和后台显示的进程( ...

  6. React Native 开发环境搭建——nodejs安装、yarn安装、JDK安装多个版本、安装Android Studio、配置Android SDK的环境变量

    一.React Native介绍 二.开发环境的搭建 2.1.Node.js安装 Node.js要求14版或更新 https://nodejs.org/en 查看版本: 2.2.yarn安装 Yarn ...

  7. spring boot过滤器实现项目内接口过滤

    spring boot过滤器实现项目内接口过滤 业务 由于业务需求,存在两套项目,一套是路由中心,一套是业务系统. 现在存在问题是,路由中心集成了微信公众号与小程序模块功能,业务系统部署了多套服务. ...

  8. 基于RL(Q-Learning)的迷宫寻路算法

    强化学习是一种机器学习方法,旨在通过智能体在与环境交互的过程中不断优化其行动策略来实现特定目标.与其他机器学习方法不同,强化学习涉及到智能体对环境的观测.选择行动并接收奖励或惩罚.因此,强化学习适用于 ...

  9. MQTT-主题基础

    MQTT主题 MQTT的主题是一个utf-8编码的字符串,最大长度65535字节,严格区分大小写 MQTT主题支持分层结构,主题分隔符用'/'表示,主题的层级长度可以为0 # 将主题划分为3个层级 ' ...

  10. 高性能、快响应!火山引擎 ByteHouse 物化视图功能及入门介绍

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 物化视图是指将视图的计算结果存储在数据库中的一种技术.当用户执行查询时,数据库会直接从已经预计算好的结果中获取数据 ...