问题描述
有 N 个学生合影,站成左端对齐的 k 排,每排分别有 N1,N2,…,Nk 个人。 (N1≥N2≥…≥Nk),第1排站在最后边,第 k排站在最前边。
学生的身高互不相同,把他们从高到底依次标记为 1,2,…,N。
在合影时要求每一排从左到右身高递减,每一列从后到前身高也递减。
问一共有多少种安排合影位置的方案?
下面的一排三角矩阵给出了当 N=6,k=3,N1=3,N2=2,N3=1时的全部16种合影方案。注意身高最高的是1,最低的是6。
输入格式
输入包含多组测试数据。
每组数据两行,第一行包含一个整数k表示总排数。
第二行包含k个整数,表示从后向前每排的具体人数。
当输入k=0的数据时,表示输入终止,且该数据无需处理。
输出格式
每组测试数据输出一个答案,表示不同安排的数量。
每个答案占一行。
数据范围
1≤k≤5,学生总人数不超过30人。
分析
首先说一下题意,这题说的很绕,其实简化版就是保证每一列单调递减,每一行单调递减,求方案数,肯定是dp啦
这个DP其实也挺玄学的,和那个生日party挺像,我们首先来确定一下dp数组的定义,它表示的肯定是合理的方案数,关键是开几维,每一维表示什么,最初的考虑,开两维,一维表示排数,另一维表示每排最多学生数,这样好像确定不了状态,所以肯定不行。那我们怎么确定状态呢,要使它能够表示出来,并且还能进行转移,而且表示的状态确定,那么什么样才能确定?
手动画图发现这几个矩形的轮廓就能确定出一个状态,转移怎么办?考虑对最后一个人分类讨论,它是排在第一行还是第二行还是第几行,因为它是最后一个人,所以多他少他都一样,于是就加上没有他的方案数,就完成了转移。因为只有五行,所以开五维就行,别小看这五维,因为有多组数据涉及到(memset),所以数组不能开的太大,每维开35都会T掉,因为355就是(52,521,875),对这个(memset)肯定T,开32左右就行了。
不要忘了longlong哦
#include<iostream>
#include<cstring>
#define g dp[i][j][c][d][e]
using namespace std;
const int N=31;
long long dp[N][N][N][N][N];
int a[6];
int main(){
int k;
while(cin>>k&&k){
memset(a,0,sizeof(a));
memset(dp,0,sizeof(dp));
dp[0][0][0][0][0]=1;
for(int i=1;i<=k;i++)
cin>>a[i];
for(int i=0;i<=a[1];i++)
for(int j=0;j<=min(i,a[2]);j++)
for(int c=0;c<=min(j,a[3]);c++)
for(int d=0;d<=min(c,a[4]);d++)
for(int e=0;e<=min(d,a[5]);e++){
if(i&&i-1>=j)g+=dp[i-1][j][c][d][e];
if(j&&j-1>=c)g+=dp[i][j-1][c][d][e];
if(c&&c-1>=d)g+=dp[i][j][c-1][d][e];
if(d&&d-1>=e)g+=dp[i][j][c][d-1][e];
if(e)g+=dp[i][j][c][d][e-1];
}
cout<<dp[a[1]][a[2]][a[3]][a[4]][a[5]]<<endl;
}
}