版权声明:本文为博主原创文章,未经博主允许不得转载。 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的题目,大致格式都是差不多的类型,唯一差别的就是关键的状态方程,对不同的题目,就需要设计出不同的状态方程,之后就都是差不多的格式了。