第一次接触数位dp,对于这种题目,数字都是很大,直接遍历肯定超时,通过对每个位子上数字的遍历可以大大减小复杂度。
dp[i][j]表示1到 i位的以j开头的数有几个满足条件的。这样理解一下,然后求出目标n的位数和各个位上的值,遍历即可。
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define INF 99999999 #define ll __int64 using namespace std; const int MAXN = 10; int dp[MAXN][MAXN]; void init() { memset(dp,0,sizeof(dp)); dp[0][0] = 1; int i,j,k; for(i=1; i<=9; i++){ for(j=0; j<=9; j++){ for(k=0; k<=9; k++){ if(j!=4 && !(j==6 && k==2)){ dp[i][j] += dp[i-1][k]; } } } } } int slove(int n) { int digit[10],i,j,k; memset(digit,0,sizeof(digit)); int len = 0; int x = n; while(1){ if(!x)break; digit[++len] = x%10; x/=10; } int ans = 0; for(i=len; i>=1; i--){ for(j=0; j<digit[i]; j++){ if(j!=4 && !(j==2 && digit[i+1]==6)) ans += dp[i][j]; } if(digit[i]==4 || (digit[i]==2 && digit[i+1]==6))//第i位是4 或者已经出现62了,不合法直接断开。 break; } return ans; } int main() { int i,j,n,m; init(); while(cin>>n>>m){ if(!n && !m)break; cout<<slove(m+1)-slove(n)<<endl;//[0,m]-[0,n) } return 0; }