[HNOI2010]合唱队 区间DP
题解:
偶然翻到这道题,,,就写了。
观察到一个数被插在哪里只受前一个数的影响,如果明确了前一个数是哪个,那么我们就可以确定大小关系,就可以知道当前这个数插在哪里,而上一个插入的数就是上一个数,所以根据这个来设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 }