动态规划之数位DP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MMMMMMMW/article/details/81265408

数位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;
}

猜你喜欢

转载自blog.csdn.net/MMMMMMMW/article/details/81265408