Mr Youngs Picture Permutations
Description
杨先生希望为他的班级拍照。学生将排成一行,每行不超过后面的行,并且行的左端对齐。例如,可以安排12名学生排列(从后到前)5,3,3和1名学生。
X X X X X
X X X
X X X
X
此外,杨先生希望每排学生安排高度从左到右减少。此外,学生身高应从后向前减少。想想看,杨先生看到,对于这个12人的例子,至少有两种安排学生的方式(数字代表高度,其中1代表最高):
1 2 3 4 5 1 5 8 11 12
6 7 8 2 6 9
9 10 11 3 7 10
12 4
杨先生想知道,对于给定排列的排列,可能有多少不同的学生安排。他尝试用长度为3,2和1的行开始计数,并计数16个排列:
123 123 124 124 125 125 126 126 134 134 135 135 136 136 145 146
45 46 35 36 34 36 34 35 25 26 24 26 24 25 26 25
6 5 6 5 6 4 5 4 6 5 6 4 5 4 3 3
杨先生认为,手动点数对于任何合理数量的学生来说都不会很有效。他通过编写计算机程序来帮助你确定一组给定行的学生的不同安排数量。
Solution
我们采用动态规划来解决此题,动态开数组(防止MLE) 设一个五维数组表示f[a1, a2, a3, a4, a5]表示1~5排分别站了a1, a2, a3, a4, a5个人时,方案数量
边界f[0, 0, 0, 0, 0] = 1
转移为:如果a1 < n1 那么f[a1 + 1, a2, a3, a4, a5] += f[a1, a2, a3, a4, a5]
如果a2 < n2 并且a2 <a1(ai表示当前第i行站了几个人,由于长度是不上升的,所以当前a2 < a1) f[a1, a2 + 1, a3, a4, a5] += f[a1, a2, a3, a4, a5]
下面以此类推。。。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int k, n[6];
int main() {
while (scanf("%d", &k), k) {
memset(n, 0, sizeof(n));
for (int i = 1; i <= k; i++)
scanf("%d", &n[i]);
unsigned long long f[n[1] + 1][n[2] + 1][n[3] + 1][n[4] + 1][n[5] + 1];
memset(f, 0, sizeof(f));
f[0][0][0][0][0] = 1;
for (int i = 0; i <= n[1]; i++)
for (int j = 0; j <= n[2]; j++)
for (int l = 0; l <= n[3]; l++)
for (int m = 0; m <= n[4]; m++)
for (int h = 0; h <= n[5]; h++) {
unsigned long long point = f[i][j][l][m][h];
if (i < n[1]) f[i + 1][j][l][m][h] += point;
if (j < n[2] && j < i) f[i][j + 1][l][m][h] += point;
if (l < n[3] && l < j) f[i][j][l + 1][m][h] += point;
if (m < n[4] && m < l) f[i][j][l][m + 1][h] += point;
if (h < n[5] && h < m) f[i][j][l][m][h + 1] += point;
}
printf("%Ld\n", f[n[1]][n[2]][n[3]][n[4]][n[5]]);
}
}