第一次做數位dp的題,雖然是入門題,真的是想了好久才想明白。
dp[i][j] 表示i位數,首位是j的數字有多少符合要求的。
很容易得到遞推公式 :
如j=4時:dp[i][j]=0
j!=4時:

如何根據數組dp求對於一個數n,0~n滿足要求的數呢?
對於一個數,例如335,對應dp[3][3],但是滿足要求的數有336,347,358……這些數超過了335……
所以通過dp[3][0],dp[3][1],dp[3][2]求得首位數字小於3的滿足條件的三位數,dp[3][0]求得的數是001,052,093...也就是所有滿足條件的一位或兩位數……我們求得所有的2xx,1xx,0xx.
接下來要求的是如334,327這類首位是3滿足條件的數字,既然首位只能是3,也就是第一位已經選完了,那么我們只要選擇滿足小於35的數字就可以了。和上面同理,我們只要求出dp[2][0],dp[2][1],dp[2][2],dp[2][3],dp[2][4]就可以求出首位小於3的兩位數。這時求得的是32x,31x,30x。
然后我們求所有小於5的數字就ok。小於5也就是dp[0],dp[1],dp[2],dp[3],dp[4]。這時求得的是53x.
到此,所有小於335且滿足的數全部求出。
但是還需要考慮的問題是62,4。
如果某一位數字為4,如77432的第三位(從左向右看)是4,那么我們依次求出6xxxx,5xxxx,...,0xxxx,76xxx,75xxx,...,70xxx,773xx,773xx,...,770xx,我們接來下需要求的是7742x....7740x,但這時所求的數是774xx的數,必然含有4,所以這時我們停止。
對於62,如果某一位數字是6,如5675,我們求完5xxx,4xxx,...0xxx之后再求56xx時,注意566x,565x,..,563x,561x,沒有562x。如果數字中含有62,如5627,那么同4,求到562x的時候就應該停止繼續求下去了。
因為求得的是小於n的數,所以求[0,n]之間的數需要求n+1.
代碼:
/*******************************************
Problem : 2089 ( 不要62 ) Judge Status : Accepted
RunId : 14481604 Language : G++ Author : G_lory
Code Render Status : Rendered By HDOJ G++ Code Render Version 0.01 Beta
*******************************************/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int dp[10][10];
int d[10];
void init()
{
dp[0][0] = 1;
for (int i = 1; i <= 7; ++i)
for (int j = 0; j <= 9; ++j)
for (int k = 0; k <= 9; ++k)
if (j != 4 && !(j == 6 && k == 2))
dp[i][j] += dp[i - 1][k];
}
int solve(int n)
{
int ans = 0;
int len = 0;
while (n) {
++len;
d[len] = n % 10;
n /= 10;
}
d[len + 1] = 0;
for (int i = len; i >= 1; --i) {
for (int j = 0; j < d[i]; ++j) {
if (d[i + 1] != 6 || j != 2)
ans += dp[i][j];
}
if (d[i] == 4 || (d[i + 1] == 6 && d[i] == 2))
break;
}
return ans;
}
int main()
{
int m, n;
init();
while (scanf("%d%d", &m, &n) == 2) {
if (n == 0 && m == 0) break;
printf("%d\n", solve(n + 1) - solve(m));
}
return 0;
}
