知识点
一个讲得很好的blog:洛谷日报第84期:数字组成的奥妙——数位dp
一 . 数位DP
数位DP是与数位相关的一类计数类DP,一般用于统计区间[l,r]满足特定条件的数位元素个数。数位指个位、十位、百位、千位等,数位DP指的就是在数位上进行动态规划。数位DP实质上就是有策略的枚举,子问题求解后进行记忆化搜索即可。
二 . 需要注意
① 记忆化 ②约束条件 ③高位前导0
三 . 数位DP模板
int dfs(int pos,int limit,int lead,int sum...各种限制)
//pos表示填数到了第几位,limit表示有无上界限制,lead表示有无前导0
{
if(!pos) return sum;//边界条件
if(!limit&&!lead&&dp[pos][...]!=-1)//如果搜过并且没有上界限制和前导0,这一位可以随便填
return dp[pos][...];//记忆化搜索
int top=limit?a[pos]:9;
//如果前一位有限制(或填到了最高位)那么这一位不能超过a这一位的大小,否则0~进制数-1随便取
int res=0;
for(int i=0;i<=top;i++)
{
if(...)//限制条件
res+=DFS(pos-1,limit&&(i==top),lead&&!i,...);
//如果所在位置位已经枚举到上界就把上界往后传,上一位有前导0而且这一位是0,那么下一位有前导0
}
if(!limit&&!lead)dp[pos][...]=res;
//因为如果枚举到上限则答案并不是这一位上所有的和,所以就不更新
//着重分析这一句话:当有前导0或有上界限制时,下一位有不能填的数,而dp数组记录的是下一位能全部填0~9的数,所以不更新.
return res;
}
int solve(int x)
{
int num=0;//位数
while(x)//这里是预处理出每一位数的上界
{
a[++num]=x%10;
x=x/10;
}
return dfs(num,1,1,...);//是否有前导0和上界限制等,注意通常统计总和的时候,初始化为1
}
int main()
{
cin>>l>>r;
cout<<solve(r)-solve(l-1)<<endl;
//可以先求1~r中满足限制的个数,再求1~l-1中满足限制的数的个数,相减即可得区间l~r的数据
}
模板题
1 .统计一个区间的数字的各个数位之和
例题: 洛谷 P4999 烦人的数学作业 / 洛谷 P1836 数页码
思路:该题要求在区间内的数字各个数位相加所得的和,但是看数据范围,可以发现