The King's Ups and Downs(组合DP)

题目链接 http://acm.sdtbu.edu.cn/vjudge/contest/view.action?cid=2199#problem/F

题目大意 : 给你n个不同的身高,把他们拍成一列,按照高低高低高低、、或者低高低高低高 排列 问你有多少种排列方法

思路 :对于第n个人,即前 n-1 个人已经排序好,第 n 个人有 n 个位置,因为第 n 个人是目前最高的人,所以他前面两人的身高应该是高低,后面两人的身高应该是低高。 假如第 n 个人站在第 j 个位置,那么他的前面有 j-1 个人,后面有 n-j 个人,首先要先   从n-1 个人中选出 j-1 个人站在第 n 个人前面 ,即有 C ( n - 1 , j - 1 )  种方法,其余的人站在第 n 个人的后面。

然后设状态dp[ i ][ 0 ]表示有 i 个人,并且第一个人通过上升得到第二个人的总的排列种数(i个人身高肯定不一样,故只考虑个数)dp[ i ][ 1 ]表示有 i 个人,并且最后一个人是通过下降得到的。

dp[ i ][ 0 ]表示站在第 n 个人后面的人的排列种数,dp[ i ][ 1 ]表示站在第 n 个人前面的人的排列种数。

很显然在人数相同的情况下,由对称性得 dp[ i ][ 0 ] = dp[ i ][ 1 ]=sum[ i ] / 2 sum[i]为i个人总的满足要求的排列数。

综上: 对于第 n 个人放到位置 j  ,有C ( n - 1 , j - 1 ) * dp[ j - 1 ][ 0 ] * dp[ n - j ][ 1 ]种情况。

#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
const int MAX=3e7+7;
typedef long long ll;

ll a[30],c[30][30],dp[30][2];
void C()
{
    for(ll i=0;i<=20;i++)
        c[i][0]=1;
    for(ll i=1;i<=20;i++)
        for(ll j=1;j<=20;j++)
            c[i][j]=c[i-1][j]+c[i-1][j-1];
    return ;
}
void DP()
{
    C();
    a[1]=1;
    dp[0][0]=dp[0][1]=1;
    dp[1][0]=dp[1][1]=1;
    for(ll k=2;k<=20;k++)
    {
        ll sum=0;
        for(ll i=0;i<k;i++)
        {
            ll j=k-i-1;
            sum+=dp[i][0]*dp[j][1]*c[k-1][i];
        }
        a[k]=sum;
        dp[k][0]=dp[k][1]=sum/2;
    }
    return ;
}

int main()
{
    DP();
    int T;cin>>T;
    while(T--)
    {
        ll k,n;
        cin>>k>>n;
        cout<<k<<" "<<a[n]<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Septembre_/article/details/88820262