动态规划专题(三)——数位DP

版权声明:此文为作者原创,若觉得写得好请点个赞再离开。当然,也欢迎在讨论区指出本文的不足,作者会及时加以改正。转载请注明地址: https://blog.csdn.net/chenxiaoran666/article/details/82948835

前言

数位 D P DP 真的是最恶心 D P DP


简介

看到那种给你两个数,让你求这两个数之间符合条件的数的个数,且这两个数非常大,这样的题目一般就是 数位 D P DP 题。

数位 D P DP 一般都用于计数


具体实现

数位 D P DP 有两种实现方法: D P DP 预处理+乱搞求答案以及记忆化搜索

个人感觉用记忆化搜索来实现要比较容易一些(第一种做法我是真的不会)。

数位 D P DP 在求解的过程中运用了前缀和的思想,即要求 l r l\sim r 范围内的解的个数,就相当于 0 r 0\sim r 范围内的解的个数减去 0 l 1 0\sim l-1 范围内解的个数即可。


模板

贴一波记忆化搜索的模板:

class Class_DigitalDP//数位DP(记忆化搜索实现)
{
	private:
		#define Size 15//如果是int范围内,数字长度不会超过15,这个Size要视题目而定
		int len,num[Size],f[Size][10];//len存储数字长度,num记录数字的每一位,f则用于记忆化
		inline void Init(LL x) {len=0;while(x) num[++len]=x%10,x/=10;num[len+1]=0;}//初始化,将x这个数字按位保存下来
		inline int dfs(int x,int s,int flag)//x记录剩余的位数,s记录当前的状态,flag记录当前是否肯定在求解的范围内(0表示不一定,1表示一定)
		{
			register int i,lim=9,w=0;//w用于统计答案
			if(!x) return OK(s);//如果剩余位数为0,就判断当前状态是否满足条件,并退出函数
			if(flag&&~f[x][s]) return f[x][s];//如果当前状态肯定在求解范围内,且已访问并求解过当前状态,就返回曾经求解出的答案
			if(!flag) (Right(num[x])&&(w+=dfs(x-1,GetStatus(s,num[x]),0),0),lim=num[x]-1;//如果不保证在求解范围内,则对这一位上的最大值单独处理,并仍不能保证在求解范围内
			for(i=0;i<=lim;++i) if(Right(i)) w+=dfs(x-1,GetStatus(s,i),1);//枚举每一个数字,如果这个数字符合条件,就继续搜索
			if(flag) f[x][s]=w;//如果当前状态肯定在求解的范围内,就将求解出的答案记录下来,实现记忆化
			return w;//返回答案
		}
	public:
		Class_DigitalDP() {for(register int i=0,j;i<Size;++i) for(j=0;j<9;++j) f[i][j]=-1;}//初始化f数组为-1
		inline int GetAns(int x) {return (void)(Init(x)),dfs(len,0,0);}//将这个数按位保存下来,然后记忆化搜索求解
}DigitalDP;

几道例题

照常贴几道例题:

第一道例题: 【BZOJ3209】花神的数论题

一道二进制的数位 D P DP ,可以发现用 D P DP 预处理得到的是一个杨辉三角形… …

第二道例题: 【BZOJ1833】[ZJOI2010] count 数字计数

比较经典的数位 D P DP 题,关键在于要判前导 0 0

第三道例题: 【HDU4507】恨7不成妻

应该是这几道题里我唯一一道写记忆化搜索的,需要加上一点恶心的数学转化。

L i n k Link

【BZOJ3209】花神的数论题 的题解 详见博客 【BZOJ3209】花神的数论题(数位DP)

【BZOJ1833】[ZJOI2010] count 数字计数 的题解 详见博客 【BZOJ1833】[ZJOI2010] count 数字计数(数位DP)

【HDU4507】恨7不成妻 的题解 详见博客 【HDU4507】恨7不成妻(数位DP)

猜你喜欢

转载自blog.csdn.net/chenxiaoran666/article/details/82948835