題目鏈接 http://acm.sdibt.edu.cn/vjudge/ojFiles/uvalive/pdf/61/6177.pdf
題意是 給定一個數n,代表着一共有n個人,且他們的身高從1到n。 要求讓這n個人站成一行,使得身高的排列呈波浪形,比如低高低或者高低高。
注意:n = 1 , ans = 1;
n = 2 , ans = 2;
動態規劃。
解題思路: 每次新加入的點k,可以看成將之前的序列分成前後兩部分,並且因爲 k是最大的,所以要求k前面數的趨勢應該是高低,k後面的趨勢應該是底高。這樣加入k後的排列數,就是前後可行排列方法數的
乘積。 枚舉k插入的位置i,以及乘上c[k][i],表示從k個數裏面取i個數的取法。
那麼怎麼計算前後可行排列的方法數呢? 經過推導後,可以證明,前面和後面的方法數是相同的,所以假設用dp[n][0]表示前n個數中以高低爲結尾的方法數,dp[n][1]表示前n個數中以底高爲開始的方法數,ans[n]表示n個人的時候的方法數,可得 dp[n][0] = dp[n][1] = ans[n] / 2;
代碼:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<string.h> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #include<stack> 9 #include<deque> 10 #include<map> 11 #include<iostream> 12 using namespace std; 13 typedef long long LL; 14 const double pi=acos(-1.0); 15 const double e=exp(1); 16 const int N = 100009; 17 18 LL a[25]; 19 LL c[25][25]; 20 LL sta[25][3],ans[25]; 21 22 void init_c() 23 { 24 LL i,p,j; 25 LL x = 1; 26 for(i = 1; i <= 20; i++) 27 { 28 x *= i; 29 a[i] = x; 30 } 31 a[0] = 1; 32 33 for(i = 1; i <= 20; i++) 34 { 35 for(j = 0; j <= i; j++) 36 { 37 c[i][j] = a[i] / a[j] / a[i - j]; 38 } 39 } 40 } 41 42 43 void init_tab() 44 { 45 LL i,p,j; 46 47 sta[0][0] = sta[0][1] = 1; //特判 k 插在第一位和最後一位的情況 48 ans[1] = 1; 49 sta[1][0] = sta[1][1] = 1; //特判 n = 1時,既可以看成是開始爲底高的方法數也可以看成是高低的方法數。 50 51 for(i = 2; i <= 20; i++) 52 { 53 for(j = 0; j <= i - 1; j++) 54 { 55 ans[i] += sta[j][0] * sta[i - j - 1][1] * c[i - 1][j]; 56 } 57 sta[i][0] = sta[i][1] = ans[i] / 2; 58 } 59 } 60 61 int main() 62 { 63 LL i,p,j,n,t; 64 LL w; 65 66 init_c(); 67 init_tab(); 68 69 scanf("%lld",&t); 70 while(t--) 71 { 72 scanf("%lld%lld",&w,&n); 73 printf("%lld %lld\n",w,ans[n]); 74 75 } 76 return 0; 77 }