动态规划——数位dp

数位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

你可能感兴趣的:(知识点总结,动态规划)