解题思路:通过分析题意,我们可以大致明白题目要我们求的就是给我们N个数,并且规定了每一排有几个数,这些数满足这样的规律:每一行从左到右逐渐递增,每一列从上到下逐渐递增,问我们满足这样规律的矩阵有多少种排法,下面我们来分析一下这个问题,当我们从高到矮排队(数字从小到大排队)的时候,不难发现:
性质1:我们当前数只能紧挨着已经填的数填,如果说不紧挨着的话,那么之前填的数和当前填的数之间就会有空隙,之后在填数的时候就会填到这个空里,但是之后填的数都比当前数要大,所以一定不满足数字是从小到大排序的规则。
性质2:从后往前,每一排的人数一定是不上升的,再次假设当前一排的人数比后一排的人数多,当把数填到多出来的位置上的时候,在后面我们就会把更大的数填到后一排中去,同样是不符合数字每一行每一列从小到大的顺序。
状态表示:f[a][b][c][d][e]表示第一排a个人,第二排b个人,第三排c个人,第四排d个人,第五排e个人时的方案数,a>=b>=c>=d>=e
状态计算:当前状态f[a][b][c][d][e]可以从f[a-1][b][c][d][e],f[a][b-1][c][d][e],f[a][b][c-1][d][e],f[a][b][c][d-1][e],f[a][b][c][d][e-1],转移过来,即直接加上这5种情况的方案数即可。
坑点1:每次计算新一个样例的时候都要讲s数组初始化为全0,因为我们每次都是会用到s数组的所有元素的值,如果不清空,上一次s数组的值就会影响到这一次的结果。
坑点2:因为我们的f数组是用的long long 类型,所以要注意空间的大小,这里N取31~33都可,超出这个范围,小了,就答案错误,大了就TLE。
上代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N =31;
long long f[N][N][N][N][N];
int k,s[5],n;
int main()
{
while(1)
{
cin>>k;
if(k==0)
break;
memset(s,0,sizeof s);//每个样例都要将s数组清空,否则会答案错误,这里很重要
for(int i=0;i<k;i++)
cin>>s[i];
memset(f,0,sizeof f);
f[0][0][0][0][0]=1;
for(int a=0;a<=s[0];a++)
for(int b=0;b<=min(a,s[1]);b++)//前面一排的人数不能超过后面一排的人数
for(int c=0;c<=min(b,s[2]);c++)
for(int d=0;d<=min(c,s[3]);d++)
for(int e=0;e<=min(d,s[4]);e++)
{
long long &v=f[a][b][c][d][e];
if(a>0&&a-1>=b)//当前状态的上一个可能的状态也必须满足当前一排人数>=前一排的人数
v+=f[a-1][b][c][d][e];
if(b>0&&b-1>=c)
v+=f[a][b-1][c][d][e];
if(c>0&&c-1>=d)
v+=f[a][b][c-1][d][e];
if(d>0&&d-1>=e)
v+=f[a][b][c][d-1][e];
if(e>0)//最后一排人数>0即可
v+=f[a][b][c][d][e-1];
}
cout<<f[s[0]][s[1]][s[2]][s[3]][s[4]]<<endl;
}
return 0;
}