HDU4489 The King’s Ups and Downs(DP+组合数)

HDU4489 The King’s Ups and Downs(DP+组合数)

Description
The king has guards of all different heights. Rather than line them up in increasing or decreasing height order, he wants to line them up so each guard is either shorter than the guards next to him or taller than the guards next to him (so the heights go up and down along the line). For example, seven guards of heights 160, 162, 164, 166, 168, 170 and 172 cm. could be arranged as:
在这里插入图片描述
or perhaps:
在这里插入图片描述
The king wants to know how many guards he needs so he can have a different up and down order at each changing of the guard for rest of his reign. To be able to do this, he needs to know for a given number of guards, n, how many different up and down orders there are:
For example, if there are four guards: 1, 2, 3,4 can be arrange as:
1324, 2143, 3142, 2314, 3412, 4231, 4132, 2413, 3241, 1423
For this problem, you will write a program that takes as input a positive integer n, the number of guards and returns the number of up and down orders for n guards of differing heights.
Input
The first line of input contains a single integer P, (1 <= P <= 1000), which is the number of data sets that follow. Each data set consists of single line of input containing two integers. The first integer, D is the data set number. The second integer, n (1 <= n <= 20), is the number of guards of differing heights.
Output
For each data set there is one line of output. It contains the data set number (D) followed by a single space, followed by the number of up and down orders for the n guards.
Sample Input
4
1 1
2 3
3 4
4 20
Sample Output
1 1
2 4
3 10
4 740742376475050

题意

给n个士兵,要求排列成高矮高矮高矮高矮…或者矮高矮高矮高矮高…假设前面已经有i-1个人,现在要放第i个人。由于i是最后放的所以他一定是最高的。他前面的结尾部分一定是高低,而后面的开始部分一定是低高。如果知道前面结尾高低的方法数为a和后面开始低高的方法数为b,那么在该位置的方法数就为a*b。我们假设前面部分有j个数,而后面部分有i-j-1个数,所以我们用dp[i][0]表示有i个人且i个人结尾为高低的方法数。dp[i][1]表示有i个人且开始为低高的方法数。所以i个人排列的状态转移方程就是ans[i]+=dp[j][0]*dp[i-j-1][1]*c[i-1][j]。c[i-1][j]为方法数。因为不确定前面j个人的标号。所以要在i-1个人中选j个人出来。当n为偶数时.假设波峰开始的序列为1010.那么把它倒置一下就变成了0101了。也就是说每一个1打头的对应着一个0打头的。
证明:我们用0表示波谷1表示波峰。
当n为奇数时。假设波峰开始的序列为10101.假设第一个数大于最后一个数。那我们把序列最后一个放到第一个位置由于第一个数大于最后一个数所以序列就变成01010。如果第一个数小于最后一个数把第一个数放到最后就行了。所以每一个1打头的对应着一个0打头的。所以dp[i][0]=dp[i][1]=ans[i]/2。
部分段落转载于原文链接:https://blog.csdn.net/bossup/article/details/9915647

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<stack>
#include<vector>
#include<functional> 
#include<map>
using namespace std;
typedef long long ll;
const int N=1e6+10,NN=1e3+10,INF=0x3f3f3f3f;
const ll MOD=1e9+7;
int n,T;
ll dp[21][2],c[21][21];
void init(){
	for(int i=0;i<=20;i++) c[i][i]=c[i][0]=1;
    for(int i=1;i<=20;i++) for(int j=1;j<i;j++) c[i][j]=c[i-1][j-1]+c[i-1][j];//Çó½×³Ë 
	dp[0][0]=dp[0][1]=dp[1][0]=dp[1][1]=1;
	ll ans=1;
	for(int i=2;i<=20;i++){
		for(int j=1;j<=i;j++) ans+=dp[j-1][0]*dp[i-j][1]*c[i-1][j-1];
		dp[i][0]=dp[i][1]=ans/2;
		ans=0;
	}
}
int main(){
	init();
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&T,&n);
		if(n==1){
			printf("%d 1\n",T);
			continue;
		}
		printf("%d %I64d\n",T,dp[n][0]*2);
	}
}

猜你喜欢

转载自blog.csdn.net/Hc_Soap/article/details/107504365