1. 程式人生 > >[HNOI2010]合唱隊 區間DP

[HNOI2010]合唱隊 區間DP

closed tchar %d eve clas bsp 區間dp con tdi

~~~題面~~~

題解:

  偶然翻到這道題,,,就寫了。

  觀察到一個數被插在哪裏只受前一個數的影響,如果明確了前一個數是哪個,那麽我們就可以確定大小關系,就可以知道當前這個數插在哪裏,而上一個插入的數就是上一個數,所以根據這個來設DP狀態。  
  f[i][j]表示滿足理想數列的i ~ j,且i是最後一個插入的方案數,g[i][j]表示滿足理想數列的i ~ j,且j是最後一個插入的方案數。

  那麽轉移就比較明顯了。

  根據最後一個插入的是i或j可以知道是從哪個區間轉移而來,然後只需要枚舉一下是否可以從f數組或者g數組轉移即可。判斷條件就是上一個插入的數與當前數的大小關系是否可以使得當前數插入到正確的位置(前面or後面)

技術分享圖片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define R register int
 4 #define AC 1100
 5 #define mod 19650827
 6 
 7 int n, ans;
 8 int s[AC], f[AC][AC], g[AC][AC];
 9 
10 inline int read()
11 {
12     int x = 0;char c = getchar();
13     while(c > 9 || c < 0) c = getchar();
14 while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar(); 15 return x; 16 } 17 18 inline void up(int &a, int b) 19 { 20 a += b; 21 if(a > mod) a -= mod; 22 } 23 24 void pre() 25 { 26 n = read(); 27 for(R i = 1; i <= n; i ++) s[i] = read();
28 } 29 30 void work() 31 { 32 for(R i = 1; i <= n; i ++)//枚舉長度 33 { 34 int b = n - i + 1; 35 for(R j = 1; j <= b; j ++) 36 { 37 int l = j + i - 1;//獲取右端點 38 if(j == l){f[j][l] = 1; continue;} 39 if(s[j] < s[j + 1]) up(f[j][l], f[j + 1][l]); 40 if(s[j] < s[l]) up(f[j][l], g[j + 1][l]); 41 if(s[l] > s[j]) up(g[j][l], f[j][l - 1]); 42 if(s[l] > s[l - 1]) up(g[j][l], g[j][l - 1]); 43 } 44 } 45 up(ans, f[1][n]), up(ans, g[1][n]); 46 printf("%d\n", ans); 47 } 48 49 int main() 50 { 51 // freopen("in.in", "r", stdin); 52 pre(); 53 work(); 54 // fclose(stdin); 55 return 0; 56 }
View Code

[HNOI2010]合唱隊 區間DP