HDU - 4489 The King’s Ups and Downs(dp)

题目链接

题目大意:

    给一个n,求身高为1-n的n个人排成 高低高低高低  或 低高低高低高 这种波浪式的形状有多少种;

思路:

    把第n个人插到前n-1个人的序列中,序列中共有n个空位,第n个人一定是最高的,如果把n插到第i个位置,那么前i-1个人序列的末尾两个人的状态一定是高低,我们把这种状态称为0状态,后n-i个人序列的开头两个人的状态一定是低高,我们把这种状态称为1状态,如果用dp[n][0]表示n个人排成末尾两个人是高低的状态的方法数,用dp[n][1]表示n个人排成开头两个人是低高的状态的方法数,那么把第n个人插到第i个位置(前n-1个人的序列是固定的)就有dp[i-1][0]*dp[n-i][1]种方法,而且因为高度各不相同,前面的i-1个人无论怎样选择都行,都可以按照相对高度排好序,所以前i-1个位置有C(n-1,i-1)种,所以把第n个人插到第i个位置总共有C(n-1,i-1)*dp[i-1][0]*dp[n-i][1]种方法,所以第n个人的序列的方法数就是把n插到前n-1个人的n个位置的总和;

  用sum[n]保存n个人的方法数,那么由于对称性,以高低结尾和以低高结尾对称,以高低开头和以低高开头对称,都是方法数的一半,而且不用担心重复,即dp[n][0]=dp[n][1]=sum[n]/2;  

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;

ll C(int n,int m){//求组合数,选人的方法数
    m = min(m,n-m);
    if(!m) return 1;
    if(m==1) return n;
    ll ans = 1;
    for(int i = 1;i <= m;i++){
        ans = ans*(n-i+1)/i;
    }
    return ans;
}

int main()
{
    ll dp[22][2],sum[22];//sum[i]保存i个人的方法数
    for(int i = 0;i <= 20;i++)
        for(int j = 0;j < 2;j++)
            dp[i][j] = 1;//因为要相乘,所以初始化为1
    memset(sum,0,sizeof(sum));
    sum[1] = 1;//一个人只有一种方法
    for(int i = 2;i <= 20;i++){
        for(int j = 1;j <= i;j++)
            sum[i]+=C(i-1,j-1)*dp[j-1][0]*dp[i-j][1];
        dp[i][0] = dp[i][1] = sum[i]/2;
    }
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;//先输入样例数,在输入m,要先输出样例数,在输出答案
        cout<<n<<' '<<sum[m]<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42754600/article/details/84195848