洛谷 P2657 windy 數(數位DP)
阿新 • • 發佈:2021-08-02
題意:如果在一個不含前導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; }