1. 程式人生 > 其它 >洛谷 P2657 windy 數(數位DP)

洛谷 P2657 windy 數(數位DP)

題意:如果在一個不含前導0的數字中,他的相鄰兩個數字的差不超過2那麼我們稱之為一個windy數,現在需要求解$$​區間內有多少個windy數

解題思路:對每一位上的數字進行預處理可以得當前最高位數字所對應的值,之後依序考慮每一位上的數字是否可一存放當前數字即可,具體解法為:對於比當前位置要小的數字,可以將其所對應的結果全部加上,對於和當前位數一樣的數字,我們需要列舉首位數字,來判斷是可以直接加入還是需要驗證是否可以加入,當找到一個不可加入的數字後,退出。

解題程式碼:

#include <iostream>
#define int long long
using namespace std;
const int mod = 1e6 + 7;
const int maxn = 1e2 + 20;
int a[maxn];
int dp[maxn][maxn];
int func(int x){
    int len = 0;
    while(x > 0ll)a[++len] = x % 10,x /= 10;
    int sum = 0ll;
    //對於位數比當前位數要小的數,那麼直接將答案加入即可
    for(int i = 1;i <= len - 1;i++){
        for(int j = 1;j <= 9;j++){
            sum += dp[i][j];
        }
    }
    //對於首位數字比給定數字要小的數,我們也是直接將答案加入
    for(int i = 1;i < a[len];i++)sum += dp[len][i];
    //對於剩下的數要去判斷方案的合法性
    //此處列舉位數
    for(int i = len - 1;i>=1;i--){
        //去找每一位前面最多能放得
        for(int j = 0;j <= a[i] - 1;j++){
            if(abs(j - a[i + 1]) >= 2)sum += dp[i][j];
        }
        //如果當前位置和上一位的差值已經比二要小了,那麼之後不管怎麼擺都是不合法的,可以直接跳出。
        if(abs(a[i + 1] - a[i]) < 2)break;
    }
    return sum;
}
signed main(){
    //對於只有一位數的情況,至少為一
    for(int i = 0;i <= 9;i++)dp[1][i] = 1ll;
    //此處列舉的是位數
    for(int i = 2;i <= 10;i++){
        //此處列舉的是首位數字,因為數字中的單獨一部分可能有前導零,所以此處要從0開始
        for(int j = 0;j <= 9;j++){
            //此處列舉的是次位的數字,如果次位數字和首位數字相差不超過二
            //那麼可以將次位數字為k的方案加入到當前方案中
            for(int k = 0;k <= 9;k++){
                if(abs(k-j) >= 2)dp[i][j] += dp[i - 1][k];
            }
        }
    }
    int n,m;
    cin >> n >> m;
    if(n > m)swap(n, m);
    printf("%lld\n",func(m + 1) - func(n));
    system("pause");
    return 0;
}