数位DP,故名思义,就是计算数字的,比如计算l----r中有多少个数中不含有连续的62或单独的4。
跟通常的DP思想基本一致,我们通过递归从最高位开始列举,一直列举到最低位,然后再从最低位反推回去即可
这期间同样需要用到记忆化搜索,毕竟没有记忆化搜索的递归只是递归,有记忆化搜索的递归才是动态规划
首先,我们建立一个数组dp[i][j],其中i代表列举到第几位,j代表列举这里的数是否是6
为什么要判断是否是6?
举个例子:
6243 7243
如果此时len = 4,而此刻是6,那么,如果len = 3时,就不能计算出现2时的次数
如果此时len = 4,而此刻是7,那么,如果len = 3时,就可以计算出现2时的次数
上面这两个算出来的结果是非重叠子问题,所以只能一一判断。
附上代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <list>
#include <map>
#include <stack>
#include <queue>
using namespace std;
#define ll long long
int dp[20][3];
int num[20];
int dfs(int len,bool is_6,bool limit)//当前位数,前面的数是否为6,前面是否到了最大值
{
if(len == 0)//如果个位是第1位,我们就虚拟一个第0位,把第0位设置为终止条件
return 1;
if(!limit && dp[len][is_6])//如果前面没到最大值,且此时需要算到的之前已经算过了,直接返回
return dp[len][is_6];
int ans = 0,maxn = limit?num[len]:9;
for(int i = 0;i <= maxn;i++)
if((is_6&&i == 2 )|| i == 4)//如果出现连续的62或者4,就跳过,因为这个不符合题意
continue;
else
ans+=dfs(len-1,i==6,limit&&i==maxn);//加上后面的
return limit?ans:dp[len][is_6] = ans;//到达最大值所得到的不能记忆化,因为这个不是重叠的
}
int solve(int n)
{
int x = 0;
while(n)
{
num[++x] = n%10;
n/=10;
}
return dfs(x,0,1);
}
int main()
{
int l,r;
while(cin >> l >> r && l+r)
{
memset(dp,0,sizeof(dp));
cout << solve(r) - solve(l-1) <<endl;
}
//cout << "AC" <<endl;
return 0;
}