排列队形,排出来大致就是这样一个形状。
怎么排队呢?先枚举判断一下
经过大脑思考,全场最高的人一定在这里。不放在这里,其至少左边或者上边会有一个空位,而那个空位是找不到一个比全场最高的人还高的人去占位的,所以top1 只有这一种选择。
接下来,看top2放哪?
👇这里可以放
这里也可以放👇
但是这里不能放,因为放这后top2 位置的左侧和上面都没有可以安置的人了。
再来看一个把,在top2 放在第一种情况下时,top3 的选择有两种
当在top2 放在第二行第一个时,top3 的选择也有两种
在这,我们或许可以猜到,排队是从身高大到小排,他的上面和左边不能有空的,有空的就意味着我要在(i-1,j)或者(i,j-1)的位置放一个身高更高的,但放到top x时,比他高的都已经排完了,没办法再进行下去,不合法了。
思路
1.按身高排序
2.从上三角开始放,放在哪行都可以,但要求是左和上不能有空的地方,且不能超过边界。
放学生的条件是
在放第i个人时,不用去管前面的人站在哪里了,只需要将满足条件的方式找出来即可。
只要该学生站在这样一行中,每列学生的身高单调性也就得以满足。换言之,我不需要关心已经站好学生的具体方案。
f (a,b,c,d,e) 定义为从每一排从左起分别站了a,b,c,d,e个学生的方案数
当安排一名新的学生时,其中a,b,c,d,e之一会增加1,从而转移到后续的阶段,符合各维度线性增长的形式。
来了一个新同学后的状态可以为以下几种情况:
-
该同学站在第一排,需要满足的条件是 a1<n1,即这一排还没有站满
f(a+1,b,c,d,e)+=f(a,b,c,d,e); -
该同学站在第二排,需要满足的条件是 a2<n2 并且 他后面的身高要大于它,即a1>a2(第一排有a1个人,第二排有a2个人,a1>a2,第一排的人数多于第二排且中间不留空,说明它的背后肯定安排了人,且身高高于他,满足要求)
a2<n2 && a1>a2
f(a,b+1,c,d,e)+=f(a,b,c,d,e); -
第三排,同理
a3<n3 && a2>a3
f(a,b,c+1,d,e)+=f(a,b,c,d,e); -
第四排,同理
a4<n4 && a3>a4
f(a,b,c,d+1,e)+=f(a,b,c,d,e); -
第五排,同理
a5<n5 && a4>a5
f(a,b,c,d,e+1)+=f(a,b,c,d,e);
代码实现
const int N = 34;
LL res[N][N][N][N][N];
int main()
{
int k;
while(cin >> k, k)
{
int num[7] = {0};
memset(res,0,sizeof res);
for(int i = 1;i <= k;i ++) cin >> num[i];
res[0][0][0][0][0] = 1;
for(int a = 0;a <= num[1];a ++)
for(int b = 0;b <= min(a,num[2]);b ++)
for(int c = 0;c <= min(b,num[3]);c ++)
for(int d = 0;d <= min(c,num[4]);d ++)
for(int e = 0; e <= min(d,num[5]);e ++)
{
LL &x = res[a][b][c][d][e];
if(a - 1 >= b) x += res[a - 1][b][c][d][e];
if(b - 1 >= c) x += res[a][b - 1][c][d][e];
if(c - 1 >= d) x += res[a][b][c - 1][d][e];
if(d - 1 >= e) x += res[a][b][c][d - 1][e];
x += res[a][b][c][d][e - 1];
}
cout << res[num[1]][num[2]][num[3]][num[4]][num[5]] << endl;
}
return 0;
}