1. 程式人生 > 其它 >【數位DP】不降數

【數位DP】不降數

【題目連結】

不降數

【題目描述】

定義一種不降數,這種數字必須滿足從左到右各位數字成小於等於的關係,如123,446。現在大家決定玩一個遊戲,指定一個整數閉區間[a,b] ,問這個區間內有多少個不降數。 【輸入】

有多組測試資料。每組只含兩個數字 a,b意義如題目描述。1a,b231

【輸出】

每行給出一個測試資料的答案,即[a,b]之間有多少不降數。

【輸入樣例】

  1 9   1 19 【輸出樣例】   9   18 數位DP通常是需要按位分情況討論的,將數num劃分為anan-1...a2a1,從最高位開始到低位,劃分為0~ai-1與ai,如果第i位上填的是0~ai-1,那麼後面的所有位數都可以填0~9,如果第i位是ai
,那麼就繼續往下討論下一位,通過這種劃分方式,可以保證我們列舉的數不超過num。 此外需要結合字首和的思想,即求區間[a,b]的不降數個數可轉化為求count(b) - count(a - 1)的值。 舉例:352    首先列舉最高位,那麼分別就是 0XX、1XX、2XX,    接下來列舉次高位,30X、31X、32X、33X、34X,    再繼續列舉最低為,350、351,    到最後一個數352特判一下是不是不降數就可以了(X的位置可以填0~9)。

那麼答案就在我們所畫的紅框內部,此外,不降數的個數,應當與 數的位數 還有 數的最高位 有關,因此我們可以使用狀態 f[i][j] 來表示 位數為i位,且最高位為j的數字的 不降數個數。

狀態表示已經解決,那麼接下來就思考如何進行狀態的計算。

對於一個i位數,最高位為j的數字,表示形式如下

                    j  k  x  x  x  ...  x

如果這個數字是不降數,那麼次高位k就應當滿足k≥j,故到此可以得出狀態轉移方程:f[i][j] = f[i - 1][j] + f[i - 1][j + 1] + f[i - 1][j + 2] + ....... + f[i - 1][9];

1 for(int i = 0;i <= 9;++i)  //初始化1位數的情況,f[1][0]也是一個不降數
2     f[1][i] = 1;
3 for(int
i = 2;i <= N;++i)  //列舉位數i 4 for(int j = 0;j <= 9;++j)//列舉首位數字j 5 for(int k = j;k <= 9;++k)//列舉次高位數字k 6 f[i][j] += f[i - 1][k];

至此,就完成位數為i位,最高位為j的數字中不降數個數的預處理。接下來,按最開始的思想,從最高位開始,列舉每一位數字的不降數並累加得出答案。

    

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 const int N = 12;
 6 int f[N][N];
 7 int num[N];
 8 int count(int n)
 9 {
10     if(n == 0)
11         return 1;
12     int cnt = 0;
13     while(n)
14     {
15         num[++cnt] = n % 10;
16         n /= 10;
17     }
18     int res = 0;
19     int now = 0,last = 0;
20     for(int i = cnt;i > 0;--i)
21     {
22         now = num[i];
23         for(int k = last;k < now;++k)  //列舉ai~ai-1,當列舉到ai則直接跳往下一位
24             res += f[i][k];
25         if(last > now)  //當上一位比當前位大時,就沒有繼續計算的必要了,如5228,到第二位時,5 > 2,必不可能成為不降數
26             break;
27         last = now;
28         if(i == 1)  //特判最後一位
29             ++res;
30     }
31     return res;
32 }
33 
34 int main()
35 {
36     for(int i = 0;i <= 9;++i)
37         f[1][i] = 1;
38     for(int i = 2;i <= N;++i)
39         for(int j = 0;j <= 9;++j)
40             for(int k = j;k <= 9;++k)
41                 f[i][j] += f[i - 1][k];
42     int n,m;
43     while(cin >> n >> m)
44         cout << count(m) - count(n - 1) << endl;
45     return 0;
46 }