动态规划——数位dp

本文探讨了动态规划中的数位DP技巧,通过实例解释其原理和优化方法。数位DP能有效解决关于数位条件的复杂问题,避免线性时间复杂度导致的超时。在使用数位DP时,应注意记忆化搜索的状态定义和边界条件,如限制数的范围以避免矛盾。此外,对于涉及前导0的题目,有时需要用10来代替。数位DP通常从低位到高位进行状态转移,答案计算时需减去起始位置的贡献。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数位dp

例题
数位dp实质是记忆化搜索

比如给出一个区间[l,r]
要你求[l,r]中有多少个数是3的倍数

for(int i=l;i<=r;i++){
   if(i%3==0) ans++;
}

但是这样时间复杂度为o(r-l-1)接近于O(n),但如果判断的条件比较复杂,很容易超时
所以我们考虑数位dp

什么是数位dp
数位dp是对每个数位单独考虑,枚举可能的状态,记录一个答案,在结尾进行记录,方便记忆化,然后return 就行了
ll dfs(int dep,bool limit,int a,int b,int c,int d){
	if(dep==0) return 1;
	if(!limit&&dp[dep][a][b][c][d]!=-1) return dp[dep][a][b][c][d];
	int up=limit?num[dep]:9;
	ll res=0;
	for(int i=0;i<=up;i++){
	    if(d==10&&i==0) res+=dfs(dep-1,limit&&i==up,10,10,10,10);
		else
		{
		if(K==2){
			if(i==d) continue;
			 res+=dfs(dep-1,limit&&i==up,b,c,d,i);
		}
		if(K==3){
			if(i==d||i==c) continue;
			res+=dfs(dep-1,limit&&i==up,b,c,d,i);
		}
		if(K==4){
			if(i==b||i==c||i==d) continue;
			res+=dfs(dep-1,limit&&i==up,b,c,d,i);
		}
		if(K==5){
			if(i==a||i==b||i==c||i==d) continue;
			res+=dfs(dep-1,limit&&i==up,b,c,d,i);
		}
	   }
	}
	if(!limit){
		dp[dep][a][b][c][d]=res;	
	}
	return res;
}

具体每道题不一样,要根据题目写状态

注意:

  • 记忆化搜索一定要加!limit,因为有上限和无上限方案数是不同的,例如问100~123有多少个数(举例子而已,具体不会有这样的题),如果第二位是1,则发现最后一位有4种填法,更新dp[1][state]=4( state是题目条件,这里只是举例,并没有条件)但当第二位是2的时候并没有4种填法,造成矛盾
  • 有些题目要判断前导0,比如问100~200之间有多少个每个数位上的数字都不同的数(即123,152合法,111,112,221非法),因为不判前导0的话会把0也当数位,(即102是非法的,因为枚举的时候是0102),这时通常用10代替前导0
 if(d==10&&i==0) res+=dfs(dep-1,limit&&i==up,10,10,10,10);
  • 一般习惯从 [ 1 , p o s ] [1,pos] [1,pos]位,但网上许多题解会从 [ 0   p o s − 1 ] [0~pos-1] [0 pos1],有所区别
  • 一般数位dp要开 long long

答案的套路: s o l v e ( r ) − s o l v e ( l − 1 ) solve(r)-solve(l-1) solversolvel1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值