数位DP学习笔记

数位DP学习笔记

  • 求给定区间 [ l , r ] [l, r] [l,r]符合条件 f ( i ) f(i) f(i)的数的个数
  • f ( i ) f(i) f(i)一般与数的大小无关,而与数的组成有关

数位DP

记忆化搜索

  • 从起点向下搜索,到最底层得到方案数,一层一层向上返回答案并累加,最后从搜索起点得到最终答案
  • 对于 [ l , r ] [l, r] [l,r]区间问题,我们一般把他转化为两次数位DP,即找` [ 0 , r ] [0, r] [0,r] [ 0 , l − 1 ] [0, l - 1] [0,l1] 两段,再将结果相减就得到了我们需要的 [ l , r ] [l, r] [l,r]

状态

  • 数位DP基本的量数字位数 p o s pos pos
  • 数位的最高位限制 l i m i t limit limit
  • 判断前导 0 0 0标记 l e a d lead lead
  • 解决数组组成问题时往往需要记录,前一位或前几位, p r e pre pre
  • 更多参量,视题意而定
前导 0 0 0标记

在许多情况下,前导 0 0 0会影响判断

我们增加一个前导 0 0 0标记

  • 当前位 l e a d = t r u e lead=true lead=true并且当前位为 0 0 0,当前位为前导 0 0 0, p o s + 1 pos+1 pos+1继续搜索
  • 当前位 l e a d = t r u e lead=true lead=true但是当前为不为 0 0 0,当前位为本数最高位, p o s + 1 pos+1 pos+1继续搜索
最高位标记 l i m i t limit limit
  • 若当前位 l i m i t = t r u e limit=true limit=true而且已经取到了能取到的最高位时,下一位 l i m i t = t r u e limit=true limit=true
  • 若当前位 l i m i t = t r u e limit=true limit=true但是没有取到能取到的最高位时,下一位 l i m i t = f a l s e limit=false limit=false
  • 若当前位 l i m i t = f a l s e limit=false limit=false时,下一位 l i m i t = f a l s e limit=false limit=false

设这一位标记为 l i m i t limit limit,当前可取的最高为 t o p top top,则下一位的标记为 l i m i t limit limit& & i = = t o p i= = top i==top

DP值的记录和使用

  • d p dp dp是在记忆化搜索的框架下进行的,每当找到一种情况我们就可以这种情况记录下来

搜到后面遇到相同的情况时直接使用当前记录的值。

  • d p dp dp数组的下标表示的是一种状态,只要当前的状态和之前搜过的某个状态完全一样

我们就可以直接返回原来已经记录下来的 d p dp dp


代码

LL dfs(int pos, int pre, bool lead, bool limit)//记搜,pos当前位,pre前一位,前导0,最高位
{
	if (pos > len) return st;	//剪枝,可更改
	if ((dp[pos][pre] != -1 && (!limit) && (!lead))) return dp[pos][pre];//记录当前值
	LL res = 0;					//暂时记录当前方案数
	int top = limit ? a[len - pos + 1] : 9;//top当前位能取到的最大值
	for (int i = 0; i <= top; i++)
	{
		//有前导0并且当前位也是前导0
		if ((!i) && lead) res += dfs(/**/, /**/, true, i == res && limit);
		//有前导0但当前位不是前导0,当前位就是最高位
		else if (i && lead) res += dfs(/**/, /**/, false, i == res && limit);
		else if (根据题意而定的判断) ret += dfs(/**/, /**/, false, i == res && limit);
	}
	if (!limit && !lead) dp[pos][pre] = res;//当前状态方案数记录
	return res;
}
LL part(LL x)//把数按位拆分
{
	len = 0;
	while (x) a[++len] = x % 10, x /= 10;
	memset(dp, -1, sizeof(dp));//初始化-1(因为有可能某些情况下的方案数是0)
	return dfs(1, -1, true, true);//进入记搜
}

模板题

windy数

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值