题目大意
有N个学生合影,站成左对齐的k排,每行分别有N1,N2…NK个人,第一排站最后,第k排站之前。学生身高依次是1…N。在合影时候要求每一排从左到右递减,每一列从后面到前也递减,一共有多少总方案。
解题思路
考虑放最低的学生的情况,我们会发现,对于所有情况,最低的学生只能放在某一行以及某一列的最后一个位置。假如这个学生不放在行末以及列末,因为他是最低的学生,那么他所在的行和列肯定不会单调。所以说,我们可以根据前面的一个状态来推出后面的一个状态,这个问题可以用dp来求解。
因为每一排的人数是非递增的。所以对于当前的状态,如果上一行人数和这一行人数不同放在行末肯定也是处于列末的,那么这一行末尾可以放人;如果相同,则只能在最后一行末尾放人。状态转移方程太长了直接看代码吧。。。
代码1
因为k最多为5,所以要开一个5维数组,但是直接开满(30^5)会mle。可以考虑一下,如果只在第一排放人,那么第一维最大30;如果在前两排放人,那么第二维最大15;。。。所以只要开一个31161187的数组就够了。
int n, s[5]; ll dp[31][16][11][8][7];
int main() {
while(~scanf("%d",&n) && n) {
zero(s); zero(dp);
for (int i = 0; i<n; ++i) scanf("%d", &s[i]);
dp[0][0][0][0][0] = 1;
for (int a = 0; a<=s[0]; ++a)
for (int b = 0; b<=s[1]&&b<=a; ++b)
for (int c = 0; c<=s[2]&&c<=b; ++c)
for (int d = 0; d<=s[3]&&d<=c; ++d)
for (int e = 0; e<=s[4]&&e<=d; ++e) {
ll &t = dp[a][b][c][d][e];
if (a>b) t += dp[a-1][b][c][d][e];
if (b>c) t += dp[a][b-1][c][d][e];
if (c>d) t += dp[a][b][c-1][d][e];
if (d>e) t += dp[a][b][c][d-1][e];
if (e) t += dp[a][b][c][d][e-1];
}
printf("%lld
", dp[s[0]][s[1]][s[2]][s[3]][s[4]]);
}
return 0;
}
代码2
如果不嫌麻烦。。。也可以动态开内存。。。
int n, s[5];
int main() {
while(~scanf("%d",&n) && n) {
zero(s);
for (int i = 0; i<n; ++i) scanf("%d", &s[i]);
ll *****dp = new ll**** [s[0]+1];
for (int a = 0; a<s[0]+1; ++a) {
dp[a] = new ll*** [s[1]+1];
for (int b = 0; b<s[1]+1; ++b) {
dp[a][b] = new ll** [s[2]+1];
for (int c = 0; c<s[2]+1; ++c) {
dp[a][b][c] = new ll* [s[3]+1];
for (int d = 0; d<s[3]+1; ++d)
dp[a][b][c][d] = new ll [s[4]+1];
}
}
}
for (int a = 0; a<=s[0]; ++a)
for (int b = 0; b<=s[1]; ++b)
for (int c = 0; c<=s[2]; ++c)
for (int d = 0; d<=s[3]; ++d)
for (int e = 0; e<=s[4]; ++e)
dp[a][b][c][d][e] = 0;
dp[0][0][0][0][0] = 1;
for (int a = 0; a<=s[0]; ++a)
for (int b = 0; b<=s[1]&&b<=a; ++b)
for (int c = 0; c<=s[2]&&c<=b; ++c)
for (int d = 0; d<=s[3]&&d<=c; ++d)
for (int e = 0; e<=s[4]&&e<=d; ++e) {
ll &t = dp[a][b][c][d][e];
if (a>b) t += dp[a-1][b][c][d][e];
if (b>c) t += dp[a][b-1][c][d][e];
if (c>d) t += dp[a][b][c-1][d][e];
if (d>e) t += dp[a][b][c][d-1][e];
if (e) t += dp[a][b][c][d][e-1];
}
printf("%lld
", dp[s[0]][s[1]][s[2]][s[3]][s[4]]);
for (int a = 0; a<s[0]+1; ++a) {
for (int b = 0; b<s[1]+1; ++b) {
for (int c = 0; c<s[2]+1; ++c) {
for (int d = 0; d<s[3]+1; ++d) {
delete dp[a][b][c][d];
}
delete dp[a][b][c];
}
delete dp[a][b];
}
delete dp[a];
}
delete dp;
}
return 0;
}