YBTOJ区间圆数(数位DP)

YBTOJ区间圆数(数位DP)

在这里插入图片描述

这也是一道数位DP的模板题,这一次是记忆化搜索的写法。
变量说明:

  • limit:当前位能否取到最大值(此题中的 1,10 进制中就是能否取到 9);
  • lead:当前位是否是前导 0;
  • pos:当前是从前向后的第 pos 位。
  • cha:0 比 1 多多少。由于可能出现负数,统统加上了 30;
  • vis(pos,cha),f(pos,cha):两个记忆化数组,记录没有限制且没有前导 0 时的情况是否搜索过,并记录搜索结果。

逐行解释:

  • if(pos==0) return cha>=30;
    如果当前所有位搜索完毕,0 比 1 多(差值大于 0)返回 1,否则返回 0。
  • if(!limit&&!lead&&vis[pos][cha]) return f[pos][cha];
    如果当前没有特殊限制并且已经搜索过了,直接返回。
  • int up=limit?a[pos]:1;
    当前位能去到的最大值。有限制那么就是原数中的 pospos 位,没有限制也只能取到 1.
  • res+=dfs(limit&(i== a[pos]), lead &(i== 0), pos-1,
    cha+(i==0?(lead?0:1):-1));
    如果之前都有限制且这一位也顶上了位,那么 limitlimit 也是1;如果之前都是前导 0 且这一位还是 0,那么这一位还是前导 0;位置就往下一位搜;计算新差值(前导 0 统统不算)。
  • if(!limit&&!lead) vis[pos][cha]=1, f[pos][cha]=res;
    当前不是特殊情况,那么就记忆下来。
    AC代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int read() 
{
	int x=0,f=1;char ch=' ';
	while(!isdigit(ch)) {ch=getchar(); if(ch=='-') f=-1;}
	while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int a[50],len,f[50][65],vis[50][65];
int dfs(bool limit,bool lead,int pos,int cha) 
{
	if(pos==0) return cha>=30;
	if(!limit&&!lead&&vis[pos][cha]) return f[pos][cha];
	int res=0;
	int up=limit?a[pos]:1;
	for(int i=0;i<=up;i++) res+=dfs(limit&(i==a[pos]),lead&(i==0),pos-1,cha+(i==0?(lead?0:1):-1));
	if(!limit&&!lead) vis[pos][cha]=1,f[pos][cha]=res;
	return res;
}
int solve(int x) 
{
	len=0;
	while(x) 
	{
		a[++len]=x%2;
		x/=2;
	}
	return dfs(1,1,len,30);
}
int main() 
{
	int l=read(),r=read();
	cout<<solve(r)-solve(l-1);
	return 0;
}

怎么样,是不是比递推简洁了很多?

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值