数位dp总结---LZM
数位DP总结 BY----LZM
当你学会了数位DP,那么你会发现,在考场上,你也写不出来。
首先给出一道例题:
给出一个区间
,求出区间内每个数字的数位和,答案 , 。
样例输入 样例输出
24 69 411
考虑枚举从
复杂度很显然是 爆chao,非常痛苦。
考虑优化,先想从
所以我们只要有一个东西能够解决从
假设从高位枚举到低位,对于每一个数位都可能有10个数字(0,1,2....8,9),为什么说可能呢,因为可能这个数字太大了,会超过 ,的上界,比如在枚举 时,我们已经枚举到了第二位,
我们枚举的---》
枚举的第一个数不能超过1,同样的,第二位不能超过二。
还有一种情况,我们枚举的---》
发现有前导零的情况,那么对于这一种我们就要记录下来。
那么怎么实现呢???
记忆化!搜!索!
我们在 中记录几个参数:
:代表目前从高位到低位枚举的位数 :代表是否有前导零 :代表是否到了上界 :代表此时的数位和
然后在开一个
为什么记忆化可以?!
因为
以下给出代码。
#include<bits/stdc++.h>
#define int long long
const int p=1e9+7;
int a[20];
int f[20][3][3][200];
int dfs(int len,int lead,int top,int sum)
{
if(!len)return sum;//没有位数了
if(!lead && !top && f[len][lead][top][sum]!=-1)return f[len][lead][top][sum];//状态被搜过了,直接回去
int bound=top?a[len]:9,res=0;//如果上界到了就枚举到这一位最多能枚举到的
for(int i=0;i<=bound;i++)res=(res+dfs(len-1,lead && !i,top && i==bound,sum+i))%p;
if(!lead && !top)f[len][lead][top][sum]=res;
return res;
}
int read()//快读就是好
{
int x=0,s=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')s=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-48;
return x*s;
}
signed main()
{
memset(f,-1,sizeof(f));//记得清空
int n=read()-1,ans=0,cnt=0;//初始n记得减1
while(n)a[++cnt]=n%10,n/=10;//从高位到低位
ans-=dfs(cnt,1,1,0);
cnt=0;
n=read();
memset(f,-1,sizeof(f));
while(n)a[++cnt]=n%10,n/=10;
ans+=dfs(cnt,1,1,0);
printf("%lld\n",(ans%p+p)%p);
return 0;
}
接着下一道题
不含前导零且相邻两个数字之差至少为
这个和上面的差不多,只是要换一下参数
记录前一个数,以及前导零和上界就可以和上一个题一样了,但是要记得一开始传参要从-2开始,因为一开始可以随便填。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=30;
int n;
int a[N];
int ans;
int f[N][N][N][N];
void init(int x)
{
memset(a,0,sizeof(a));
n=0;
while(x)a[++n]=x%10,x/=10;
}
int dfs(int len,int pos,int lead,int top)
{
if(!len)return 1;
if(!top && !lead && f[len][pos][lead][top]!=-1)return f[len][pos][lead][top];
int bound=top?a[len]:9,res=0;
for(int i=0;i<=bound;i++)
{
if(abs(pos-i)<2)continue;
if(lead && !i)res+=dfs(len-1,-2,1,top && i==bound);
else res+=dfs(len-1,i,0,top && i==bound);
}
if(!lead && !top)f[len][pos][lead][top]=res;
return res;
}
int read()
{
int x=0,s=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')s=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-48;
return x*s;
}
signed main()
{
memset(f,-1,sizeof(f));
int x=read();
x--;
init(x);
ans-=dfs(n,-2,1,1);
x=read();
init(x);
memset(f,-1,sizeof(f));
ans+=dfs(n,-2,1,1);
cout<<ans;
return 0;
}
那么其他的就差不多了。
总结:记忆化搜索!
然后你就可以AK
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】