题目链接:点击打开链接
题意:给了你n名不同身高的士兵,求有多少种方法是把他们按波浪状(高低高或低高低)排列。
既然是要按波浪状排列,而且士兵的身高有各不相同,那么我们可以枚举枚举身高最高(或最低)的士兵所在的位置,左边肯定 .... 低高低,右边肯定是低高低 .... 。
如 集合{1,2,3,4,5},当5在第4个位置是,左边有三个数,右边有一个数,这四个数左右分配的方法数就是C(4 , 1 ) 或C(4,3) ,右边这种情况下就只有1个数,没啥好说的,左边3个数,而且是以低结尾的,我们用dp[3][0]表示,我们可以用同样的方法递归求出。
在求解时最后一个数字可以是高,也可能是低,即答案=dp[n][0]+dp[n][1],其实枚举出最高士兵位置后,如果右边人数为偶数时这个序列以高结尾,即dp[n][1],奇数相反,为dp[n][0]。
而且在递归求解过程中要求解大量重复的dp[i][0],dp[i][1],我们只要从小开始求,就可以避免重复求解,其实这也算是记忆化吧。
具体详见代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
LL dp[30][2];
LL C(int a,int b)//组合数学,求C(a,b)
{
LL fm=1,fz=1;
for(int i=0;i<b;i++)
{
fm*=(b-i);
fz*=(a-i);
}
return fz/fm;
}
void init()
{
int i,j;
dp[0][0]=1;
dp[0][1]=1;
dp[1][1]=1;
dp[1][0]=1;
dp[2][1]=1;
dp[2][0]=1;
for(i=3;i<=20;i++)
{
dp[i][0]=0; dp[i][1]=0;
for(j=1;j<=i;j++)
{
int l=j-1;
int r=i-j;
LL ans=C(i-1,l);
ans=ans*dp[l][0]*dp[r][0];
if(r&1) dp[i][1]+=ans;
else dp[i][0]+=ans;
// cout<<ans<<endl;
}
//cout<<dp[i][0]+dp[i][1]<<endl<<endl;
}
}
int main()
{
init();
int n,m;
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
LL ans=dp[m][1]+dp[m][0];
if(m==1) ans=1;
printf("%d %lld\n",n,ans);
}
return 0;
}