【算法笔记】数位dp
·前言
当我们遇到某些题目的时候(比如像让你统计l——r这一个区间内的数字和以及满足条件的数有几个这一类的题目),常常会因为区间太大而无法计算。这时候,我们就需要用上我们伟大的数位dp啦!
数位dp的实质就是换一种暴力枚举的方式,使得新的枚举方式满足dp的性质,然后记忆化就可以了。(nm这本质上不还是记忆化搜索)
(PS:虽然说数位dp是可以用正常的递推算法来写的,但是递推不仅难想还难调的一批,没准就一不小心人就没了,还是用记忆化搜索板子攻克这一切吧)
(PPS:记忆化搜索背个板子就好,但是特别难查错qwq)
·基本思维
由于递推版的数位dp本质上就是个找规律枚举状态的过程,其实和记忆化搜索差不了多少(而且我不会写QAQ)。所以该篇博就讲记忆化搜索的方法就over了……
考虑一下枚举每一位(pos)的数字(记得确定一下上界(limit)qwq),以及在中途判断是否存在有前导零(lead),接下来就可以愉快地记忆化搜索啦~
state状态需要根据题目的不同而确定哦qwq
给个板子趴(
#include<bits/stdc++.h> using namespace std; int a[20]; long long dp[20][state]; long long dfs(int pos,int state,bool lead,bool limit){ if(!pos)return 1; if(!limit&&!lead&&dp[pos][state]!=-1)return dp[pos][state]; int up=limit?a[pos]:9; long long ans=0; for(int i=0;i<=up;i++){ if() ... else if()... ans+=dfs(pos-1, ,lead&&i==0,limit&&i==a[pos]) } if(!limit&&!lead)dp[pos][state]=ans; return ans; } long long solve(long long x){ int pos=0; while(x){ a[++pos]=x%10; x/=10; } return dfs(pos,1,1); } int main(){ long long l,r; while(~scanf("%lld%lld",&l,&r)) printf("%lld\n",solve(r)-solve(l-1)); }
·题目汇总
HDU3652 B-Number
01 Luogu4317/BZOJ3209
02 BZOJ 1833 ZJOI2010 Count
03 HDU4734 f(x)
04 Codeforces 55D Beautiful Number
05 2012 Multi-University Training Contest 6 XHXJ’s LIS
06 HDU4507
*依旧莫得链接,以后把题解整出来再说吧qwq