[BZOJ 1026] SCOI 2009 windy數 · 動態規劃

本文轉自@http://blog.csdn.net/ycdfhhc/article/details/43956247

參考了衆神犇的題解後,AC了人生第一道數位DP。

友鏈:http://hzwer.com/3503.html

首先很容易可以想到,設calc(X)表示1..X閉區間內的windy數,那麼答案肯定是calc(B)-calc(A-1)

那麼剩下就是如何計算calc(X)的問題了。

預處理DP很水:f[i][j]表示i位的數,最高位數值爲j的情況下有多少個windy數,那麼遞推式:f[i][j]+=f[i-1][k] | k=0..9 & Abs(j-k)>=2

這題的關鍵是分解數據,筆者做的時候還是個蒟蒻,所以碰到一些問題一下子沒搞懂,所以寫在這裏:比如f[3][4],表示4開頭的三位數有多少個windy數,實際範圍是400…499。

筆者做題的時候對對數字拆解還不怎麼理解,所以在此也以2682這個數寫個栗子:

首先,拆成0..999,1000…1999,2000..2999,此時2999已經超過了2682,所以對2000…2682還要細分,此時我們可以把開頭的2拿掉,又變成了0…682,和2000…2682是一個效果。那麼0..682再向上述過程一樣拆解。

如1000…1999這個值怎麼算呢?這不就是f[4][1]嗎?但是注意,由於不能有前導0,所以0..999的windy數並不等於f[4][0],而是0..99,100..199······900..999 同上述方法一樣處理,這個操作雖然講解起來有點繁瑣,但實際代碼很簡單(line26~29)

 

還有另外兩個注意點,坑的筆者多交了兩次:

1、0要特判。(否則WA的很慘還不明不白的。。。)

2、當處理到某一位的原本值已經不符合要求的時候,直接Break,否則會多計算。還是舉個栗子吧:1457,在做到第3位的時候,5和前面的4已經衝突,所以接下來再處理的145..開頭的數肯定都是不符合要求的,再加上去就是多算了。

  1. #include <stdio.h>  
  2. #include <algorithm>  
  3. #include <string.h>  
  4. #include <iostream>  
  5. using namespace std;  
  6. #define ll long long  
  7.   
  8. ll a,b,f[11][10],d[11];  
  9.   
  10. void init(){  
  11.     cin>>a>>b;  
  12.     memset(f,0,sizeof f);  
  13.     for (int i=0;i<=9;i++)  
  14.         f[1][i]=1;    
  15.     for (int i=2;i<=10;i++)  
  16.         for (int j=0;j<=9;j++)  
  17.             for (int k=0;k<=9;k++)  
  18.                 if (abs(j-k)>1) f[i][j]+=f[i-1][k];  
  19. }  
  20.   
  21. ll calc(int x){  
  22.     if (!x) return 0;   //0特判   
  23.     int ans,n;  
  24.     ans=n=0;  
  25.     while (x) d[++n]=x%10,x/=10;  
  26.     for (int i=1;i<n;i++)  
  27.         for (int j=1;j<=9;j++)  
  28.             ans+=f[i][j];  
  29.     for (int i=1;i<d[n];i++) ans+=f[n][i];  
  30.     for (int i=n-1;i>0;i–){  
  31.         for (int j=0;j<d[i];j++)  
  32.             if (abs(d[i+1]-j)>1) ans+=f[i][j];  
  33.         if (abs(d[i]-d[i+1])<=1) break;      //原數有衝突時直接退出   
  34.     }  
  35.     int can=1;  
  36.     for (int i=2;i<=n;i++)       //特判 x本身這個數   
  37.         if (abs(d[i]-d[i-1])<=1) {can=0;break;}  
  38.     ans+=can;  
  39.     return ans;  
  40. }  
  41.   
  42. int main(){  
  43.     init();  
  44.       
  45.     cout<<(calc(b)-calc(a-1))<<endl;  
  46.       
  47.     return 0;  
  48. }  


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章