re:从零开始的数位dp
Posted mxang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了re:从零开始的数位dp相关的知识,希望对你有一定的参考价值。
起源:唔,,前几天打cf,edu50那场被C题虐了,决定学学数位dp,此文持续更新,9.16号之前会更新完的。
ps:我也什么都不会遇到一些胡话大家不要喷我啊。。。
数位dp问题:就是求在区间l到r上满足规定条件的数的个数。
ex1:hdu3555
题意:给你n,求从一到n中有多少个数不包含“49”。(t<=1e4,n<=2^63-1)
首先数位dp顾名思义就是对数位进行dp嘛,所以dp数组的第一维我们用来保存数字的位数,第二位我们用来判定当前位是否为4,
所以就是 dp[20][2]; 这个样子。 在这之前我们先考虑一下常规的搜索思路,一件非常显然的事情,在[1,1000]和[1001,2000]以及所有类似区间里符合要求的数都是一样的,这样我们就可以通过记忆化的方式来保存某些结果。
先给出solve函数
1 ll solve(ll num){ 2 int k = 0;//记录数位 3 while(num){ 4 k++; 5 digit[k]=num%10; 6 num/=10; 7 } 8 return dfs(k,false,true); 9 }
这个很好理解嘛,保存这个数各位上的数字。然后我们就可以进行记忆化搜索了,
dp[20][2]:表示 1.有4的时候有几个含有49, 2.没有4的时候,有几个含有49。
ll dfs(int len,bool if4,bool limit){
//当前是第几位,上一位是否是4,上一位是否是上界
if(len==0)//统计完了直接返回1
return 1;
if(!limit&&dp[len][if4])//不是上界并且这种情况已经统计过
return dp[len][if4];
ll cnt=0,up_bound=(limit?digit[len]:9);//up_bound是当前位能满足的最大值,如果上一位是上界的话,当前位最大只能取到当前位的数字,如果不是,当前位可以从0取到9
for(int i=0;i<=up_bound;i++){
if(if4&&i==9)
continue;//上一位是4并且这一位是9,GG了啊
cnt+=dfs(len-1,i==4,limit&&i==up_bound);//上一位是上界的情况下我们才会考虑这一位是否是上界
}
if(!limit)//不是上界,属于通用的情况,我们进行赋值
dp[len][if4]=cnt;
return cnt;
}
最后结果差分一下就好。
ex2:hdu2089
和上道题几乎一样,条件是没有“4”并且没有“62”,
这时候我们掏出上一道题的板子了嘛肯定要,只需要在判断时加入一句话就行,在代码中加上注释了,就不做多解释了
#include <bits/stdc++.h> using namespace std; int n,m; int digit[10]; int dp[10][2]; int dfs(int len,bool if6, bool limit){ if(len==0) return 1; if(!limit&&dp[len][if6]) return dp[len][if6]; int cnt = 0,up_bound = (limit?digit[len]:9); for(int i=0;i<=up_bound;i++){ if(i==4)//如果遇到四就直接GG continue; if(if6&&i==2) continue; cnt+=dfs(len-1,i==6,limit&&i==up_bound); } if(!limit) dp[len][if6]=cnt; return cnt; } int solve(int num){ int k = 0;//记录数位 while(num){ k++; digit[k]=num%10; num/=10; } return dfs(k,false,true); } int main(){ while (scanf("%d%d",&n,&m)&&(n+m)) { cout << solve(m) - solve(n - 1) << endl; } }
ex3:
以上是关于re:从零开始的数位dp的主要内容,如果未能解决你的问题,请参考以下文章