咸鱼了一个月,不能在闲下去了。
第一次做数位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; } }