数位DP?记忆化罢了!
我看了半天的数位 DP,DP 没学会,人倒是麻了。
解决什么
一般用于求解给你一个区间
这种题目还是蛮常见的,我们一般情况下暴力只能拿一少部分分,之前我看着那个
然后就有了今天让我麻了的数位 DP。
思想
题目中给的让我们难以下手,我们不如转化一下:求
这样就好处理多了,当然也可以从
然后我们把要求的
在分解的时候,我们用一个数组
我们在 dfs 的时候,传的参数至少是包含 pos
当前填到第几个数以及 limit
也就是当前点是否有限制,如果有的话,我们在后面遍历当前点填的数的时候直接调用之前的
当然我们在 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 板子题。
我们设
我们用 ?
来表示当前点没有填入,假设我们现在从左往右填,当前的状态是 10101?????
,我们 dfs 完以后,直接存入 10011?????
这种的,我们可以发现,后面问号的可能性是一样的,也就是说,他们得到的答案是一样的,那么我们就可以进行记忆化了。
我们对于给定的
由于这里的情况很少,只有
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;
}
作者: 北烛青澜
出处:https://www.cnblogs.com/Multitree/p/17487561.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战