题意:(N(N<=30))个人排队,规定要站成(K(K<=5))排,且每排站(a_i)个人((a_1+a_2+...+a_k=N)),队伍要保证从后到前,从左到右身高都单调递减(保证每个人的身高都不一样).求有多少种方案?
分析:假设每次都有5排,不足排视作站0人.从高到低,用(1)~(N)给每个人编号,我们从第1号(最高的人)开始放起,我们只要知道当前的队伍站成了什么样子,就能知道要如何放下一个人.
设(f(b_1,b_2,b_3,b_4,b_5))表示第一排已经站了(b_1)个人......第5排已经站了(b_5)个人的方案数.
(i=1)且(b_i<a_i)时,(f(b_1+1,b_2,b_3,b_4,b_5)+=f(b_1,b_2,b_3,b_4,b_5))
(i=2)且(b_i<a_i)且(b_{i-1}>b_i)时,(f(b_1,b_2+1,b_3,b_4,b_5)+=f(b_1,b_2,b_3,b_4,b_5))
(i=3,4,5)同第2排一样.
初始状态(f(0,0,0,0,0)=1),目标状态(f(a_1,a_2,a_3,a_4,a_5))
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
LL a[10],f[31][31][31][31][31];
int main(){
while(1){
int k=read();if(!k)break;
memset(a,0,sizeof(a));
for(int i=1;i<=k;i++)a[i]=read();
for(int b1=0;b1<=a[1];b1++)
for(int b2=0;b2<=a[2];b2++)
for(int b3=0;b3<=a[3];b3++)
for(int b4=0;b4<=a[4];b4++)
for(int b5=0;b5<=a[5];b5++)
f[b1][b2][b3][b4][b5]=0;
f[0][0][0][0][0]=1;
for(int b1=0;b1<=a[1];b1++){
for(int b2=0;b2<=a[2];b2++){
for(int b3=0;b3<=a[3];b3++){
for(int b4=0;b4<=a[4];b4++){
for(int b5=0;b5<=a[5];b5++){
for(int i=1;i<=k;i++){
if(i==1&&b1<a[1])
f[b1+1][b2][b3][b4][b5]+=f[b1][b2][b3][b4][b5];
if(i==2&&b2<a[2]&&b1>b2)
f[b1][b2+1][b3][b4][b5]+=f[b1][b2][b3][b4][b5];
if(i==3&&b3<a[3]&&b2>b3)
f[b1][b2][b3+1][b4][b5]+=f[b1][b2][b3][b4][b5];
if(i==4&&b4<a[4]&&b3>b4)
f[b1][b2][b3][b4+1][b5]+=f[b1][b2][b3][b4][b5];
if(i==5&&b5<a[5]&&b4>b5)
f[b1][b2][b3][b4][b5+1]+=f[b1][b2][b3][b4][b5];
}
}
}
}
}
}
printf("%lld
",f[a[1]][a[2]][a[3]][a[4]][a[5]]);
}
return 0;
}