SCOI2009 Windy數
阿新 • • 發佈:2018-09-07
rep printf 代碼 using con 不能 turn queue std
傳送門
這道題是數位DP的入門題。
什麽是數位Dp?簡單來說,數位DP就是用於解決在一個給定區間之內,有多少個數滿足條件的一種DP。其中數的多少和數的大小無關,而與數的結構有關。我們先計算出來[0,r]中符合條件的數,再計算出[0,l-1]中符合條件的數就可以。
以這道題為例,其實在數位之間DP是很好想的,我們枚舉相鄰的兩位,符合條件就把這些方案數全都加上。所以我們設dp[i][j]表示長度為j的數,以j為最高位,有多少個windy數。
那麽方程就是dp[i][j] = sigma(dp[i-1][k]),其中abs(k-j)>= 2;
之後的問題是,我們怎麽求一段區間之內所有符合的數有多少個呢?
這裏提供一種可以計算[0,l)的符合條件數的方法,到時候計算的時候只要相應+1即可。
對於一個數,我們把它每一位存在一個數組的單元中。首先對於每個長度比當前數小的,我們全部加上,因為肯定是符合的(註意第一位不能是0).然後對於位數相同的,我們只要從高位開始,枚舉哪一位是小於當前數的。這樣一直枚舉到最後一位就可以。註意這裏必須符合兩個條件,第一個是從第二位開始必須保證你當前枚舉的每一位都和上一位的絕對值相差大於等於2,否則你就統計了不合法情況。第二個就是你當前的邊界本身必須符合條件,否則你相當於一直在統計不合法情況。
這樣就可以啦。上代碼理解一下。
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> #include<cstring> #define rep(i,a,n) for(int i = a;i <= n;i++) #define per(i,n,a) for(int i = n;i >= a;i--) #define enter putchar(‘\n‘) using namespace std; typedef long long ll; const int M = 1000005; int a,b,dp[20][20],ans,af[20],len; int read() { int ans = 0,op = 1; char ch = getchar(); while(ch < ‘0‘ || ch > ‘9‘) { if(ch == ‘-‘) op = -1; ch = getchar(); } while(ch >=‘0‘ && ch <= ‘9‘) { ans *= 10; ans += ch - ‘0‘; ch = getchar(); } return ans * op; } void init() { rep(i,0,9) dp[1][i] = 1;//0~9都是windy數 rep(i,2,10) rep(p,0,9) rep(q,0,9) { if(abs(p-q) < 2) continue; dp[i][p] += dp[i-1][q];//這裏是數位Dp } } int solve(int x) { int cur = 0; memset(af,0,sizeof(af));len = 0; while(x) { af[++len] = x % 10; x /= 10;//把一個數拆分 } rep(i,1,len-1) rep(j,1,9) cur += dp[i][j//計算所有位數比它少的]; rep(i,1,af[len]-1) cur += dp[len][i];//第一位 per(i,len-1,1)//從第二位開始 { rep(j,0,af[i]-1) { if(abs(j - af[i+1]) < 2) continue; cur += dp[i][j];//加上合法情況 } if(abs(af[i+1] - af[i]) < 2) break;//如果原數不合法就跳出 } return cur; } int main() { a = read(),b = read(); init(); ans = solve(b+1) - solve(a); printf("%d\n",ans); return 0; }
SCOI2009 Windy數