HDU

咸鱼了一个月,不能在闲下去了。

第一次做数位dp,好难!虽然是入门题。

https://vjudge.net/problem/HDU-2089

思路:从高到地枚举每一位可能出现的数字,在设置一个变量state表示当前位前面是否是6,那么dp[len][state]代表长度为len状态为staste时的解的个数。首先要把所求的数拆开,这样做的目的是知道每一位能取的最大值。再设置一个参数fp表示当前位选择的数字是不是当前位可以取到的最大值。举个例子,如果我们要求6734,那么我们肯定要求6xxx,5xxx,4xxx,3xxx,......(加法原理)在这种情况下每一种取法的后三位的答案一定是一样的,这种时候直接加起来就好(dp[len-1][state]),但是如果首位是6就有点特殊了这个时候不能直接加上dp[i-1][state]了,因为后三位有了限制不可以取到0-999。具体看代码就懂了。

#include<bits/stdc++.h>
#define _for(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int mod =1e6+7;
double esp=1e-6;
int INF =0x3f3f3f3f;
const int inf = 1<<28;
const int MAXN=1e5+10;
int p[10];
int dp[10][2];
int dfs(int len,bool state,bool fp)
{
    if(len==0)return 1;
    if(!fp&&dp[len][state]!=-1)return dp[len][state];//记忆化
    int fpmax=fp?p[len]:9;//当前位取到的最大值
    int ans=0;
    for(int i=0;i<=fpmax;i++)
    {
        if(i==4||(state&&i==2))continue;
        ans+=dfs(len-1,i==6,fp&&i==fpmax);
    }
    if(!fp)dp[len][state]=ans;
    return ans;
}
int solve(int n)
{
    int len=0;
    while(n)
    {
        p[++len]=n%10;
        n/=10;
    }
    return dfs(len,0,1);

}
int main()
{
    int a,b;
    memset(dp,-1,sizeof(dp));
    while(~scanf("%d%d",&a,&b))
    {
        if(a==0&&b==0)break;
        cout<<solve(b)-solve(a-1)<<endl;
    }
}
原文地址:https://www.cnblogs.com/kayiko/p/12288230.html