HDU 2089的類似題(62換成了38) 數位dp解釋

鏈接
來源:牛客網

杭州人稱傻乎乎的人爲62,而嘟嘟家這裏沒有這樣的習俗。

相比62,他那裏的人更加討厭數字38,當然啦,還有4這個

數字!所以啊,嘟嘟不點都不想見到包含38或者4的數字。

每次給出一個區間[n,m],你能找到所有令人討厭的數字嗎?

輸入描述:

多組輸入輸出;
輸入的都是整數對n、m(0

#include <iostream>
#include <cstring>
using namespace std ;
const int len = 7 ;
int dp[8][10] , bit[8] ;//全局變量初始爲0,dp[x][y]數組表示長爲x的以y打頭的所有數中“合法”數的個數,
// 注意首位爲0的數也看作x位,例如0123看作4位數,
// 原因:如果不考慮以0開頭的數那麼dp[3][3] = dp[2][9] + dp[2][8] + ...+ dp[2][2] + dp [2][1] + dp[1][9] + dp[1][8] + ... + dp[1][2] + dp[1][1]
// 若把所有一位數看作以0開頭的2位數則實際上dp[3][3] = dp[2][9] + dp[2][8] + ...+ dp[2][2] + dp [2][1] + dp[2][0]
// 所以認爲dp[x][0] = dp[x-1][0...9]
// 我們只需要把dp[2][0...9]這些加起來就好了
int n , m ;

void Get_dp() {
    dp[0][0] = 1 ;
    for ( int i = 1 ; i <= len ; ++i )//長度i從1到7
        for ( int j = 0 ; j <= 9 ; ++j )//數值j從0到9
            if ( j != 4 )//排除j等於4的情況
                for ( int k = 0 ; k <= 9 ; ++k )//第二個數值k從0到9
                    if ( !( j == 3 && k == 8 ) )//排除j,k爲3,8的情況
                        dp[i][j] += dp[i - 1][k] ;//i從1開始所以i-1不會越界且由於是全局變量默認值爲0,所以免去了對i-1是否越界的討論

                    /**
                    * 從長爲1位的數,開頭的數從0到9,填表
                    * 狀態轉移方程:
                    * dp[x][y] = 0; 若y==4
                    *           dp[x-1][0...9]; 若y!=3(相當於在所有x-1位的合法數前面加了個不是3的數,肯定也是合法數)
                    *           dp[x-1][0...9] - dp[x-1][8];    若y==3則要排除掉第二位爲8的情況
                    */
}

int solve( int n ) {//根據表來得出從1到n(實際上沒有包括n,因爲下面有一句for ( int j = 0 ; j < bit[i] ; ++j )用的小於號,我們先不考慮這細節)的所有合法數的個數
    memset( bit , 0 , sizeof( bit ) ) ;//將n分解放入bit數組中
    int top = 0 ;//(從n的底位到高位倒序放入)
    while ( n ) {
        bit[++top] = n % 10 ;
        n /= 10 ;
    }

    int ans = 0 ;//保存所有合法數個數
    for ( int i = len ; i >= 1 ; --i ) {//i從數的最高位開始向下
        for ( int j = 0 ; j < bit[i] ; ++j )
            if ( !( bit[i + 1] == 3 && j == 8 ) )
                ans += dp[i][j] ;//排除上一位是3且這一位以8開頭的情況
        if ( bit[i] == 4 || ( bit[i + 1] == 3 && bit[i] == 8 ) )
            break ;//若這一位是4則跳出循環
    }
    /**
    * 這裏舉個例子解釋一下:
    * 假設輸入n爲243
    * 那麼我們應該將1...243分解成     000...199       200...239       240...242               243
    * 對應一下其實就是              dp[3][0...1]    dp[2][0...3]    dp[1][0...2]            dp[0][]恆爲0(所以代碼中不管數字n是否合法都沒有考慮到,最終返回值其實是1...n-1的所有合法數個數)
    * 註解                            (1)             (2)             (3)不合法,之後的也是        (4)不合法
    * 
    * (1)從最高位開始
    * (2)00...39中的所有合法數前面加一個2還是合法數
    * (3)0...2的前面加上一個4出問題了!!,所有的0...2中的合法數變得不合法,應該排除,接下來所以位的分解前面也會加上4,所以直接跳出循環
    *   if ( bit[i] == 4 || ( bit[i + 1] == 3 && bit[i] == 8 ) )
    *       break ;//若這一位是4則跳出循環
    * (4)這裏爲什麼單獨分開來?提示:“小於”號“<”
    * 
    * 其實這些分法都是受到了上面的:
    * for ( int j = 0 ; j < bit[i] ; ++j )
    * 的影響(注意小於號),也就是按照這條語句來分的
    * 需要仔細想想
    */
    return ans ;
}

int main() {
    Get_dp() ;//打表
    while ( cin >> n >> m && ( n || m ) )
        cout << m - n + 1 - ( solve( m + 1 ) - solve( n ) ) << endl ;
    return 0 ;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章