dp:数位dp的不详细解释(hdu3555)

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

首先来一道题目

hdu3555 : http://acm.hdu.edu.cn/showproblem.php?pid=3555

题目大意是让你在1~n之间找到含有'49'的数字,比如2491,88949,4900等

之后代码:

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
ll dp[50][3];
ll countt(int sw[],int w,int st,bool bj)	//w是dp到了第几位,st是状态,bj是判断该位是否有上限
{
	if(w == 0)
	{
		if(st == 2)
			return 1;
		else
			return 0;
	}
	if(!bj && dp[w][st]!=-1)
		return dp[w][st];
	int e,i;
	ll ans=0;
	e = bj ? sw[w] : 9;		//由bj进行判断,有上限就是原来的数字n上限,没有,上限就是9
	for(i=0;i<=e;i++)		//关键的状态方程
	{
		if(st==2 || (st==1 && i==9))
			ans += countt(sw,w-1,2,bj&&i==e);
		else if(i == 4)
			ans += countt(sw,w-1,1,bj&&i==e);
		else
			ans += countt(sw,w-1,0,bj&&i==e);
	}
	if(!bj)	dp[w][st] = ans;	//打表记录
	return ans;
}
ll play(ll n)
{
	int sw[50];
	int l=0;
	while(n != 0)		//数位分离
	{
		sw[++l] = n%10;
		n /= 10;
	}
	return countt(sw,l,0,1);
}
int main()
{
	int T;
	scanf("%d",&T);
	memset(dp,-1,sizeof(dp));
	while(T--)
	{
		ll n;
		scanf("%lld",&n);
		printf("%lld\n",play(n));
	}
	return 0;
}

再解释关键的状态方程:

       第一个判断,st==2 表示之前枚举的数位中已经出现了49,(st==1 && i==9)表示在现在这一位的前一位是4,且现在这一位枚举到了9,意思就是出现了49,进入st==2的递归。

       第二个判断,i==4表示现在的数位枚举到了4,进入st==1的递归

       第三个判断,没有符合以上情况,进入st==0的递归

       而 bj&&i==e 则是用来判断这一位的这一位的下一位的上限是多少

       之后就是打表记录了

总结:

       其实说实话,数位dp就是一个打表的过程,这免去了很多不必要的重复计算,而除去这题之外,其他的数位dp的题目,大致格式都是差不多的类型,唯一差别的就是关键的状态方程,对不同的题目,就需要设计出不同的状态方程,之后就都是差不多的格式了。

猜你喜欢

转载自blog.csdn.net/qq1965610770/article/details/79910240