AcWing 271. 杨老师的照相排列(动态规划)

[题目概述]

有 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。

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
输入格式

输入包含多组测试数据。
每组数据两行,第一行包含一个整数 k 表示总排数
第二行包含 k 个整数,表示从后向前每排的具体人数。
当输入 k=0 的数据时,表示输入终止,且该数据无需处理。

输出格式

每组测试数据输出一个答案,表示不同安排的数量。
每个答案占一行。

数据范围

1 ≤ k ≤ 5,学生总人数不超过 30 人。

输入样例:
1
30
5
1 1 1 1 1
3
3 2 1
4
5 3 3 1
5
6 5 4 3 2
2
15 15
0
输出样例:
1
1
16
4158
141892608
9694845
  • 分析题目
    题目让我们求所有排列的方案数量,也是一个动态规划的问题,本题相较于其他题来说,最大的难点就是在状态表示上。
    根据样例我们可以推出来两个性质(按身高序号从小到大模拟,就可以发现这个规律)
    • 每行的人数是一次递减的
    • 每行都是排列紧密的,不留空
      最终排列下来是一个倒三角的形状,我们可以根据这个形状来分类求,那么我们的集合就是 所有第一行有a人,第二行有b人,第三行有c人,第四行有d人,第五行有e人的方案,这样我们就可以画出图了
      AcWing 271. 杨老师的照相排列(动态规划)_第1张图片
      接下来就是状态计算了,按照我们的划分依据,从最后一个不同的元素入手,本题就是最后一个人的位置,是在一至五排中的哪一排。假设最后一个人x在第一排,那么这个集合中所有的方案第一排的最后一个人都是x,那如果将其去掉的话,此集合中方案的个数是不变的,此时我们可以将二者转化。AcWing 271. 杨老师的照相排列(动态规划)_第2张图片
      现在我们就大致分析完成了。
  • 完整代码(注解)
#include 
#include 
#include 
#include 
using namespace std;

const int N = 31;
int n;
long long f[N][N][N][N][N]; // 最后测试发现会爆int,需要用long long存
int main () {
	cin >> n;
	// 为0就可以结束程序了
	while (n) {
		int s[5] = {0};
		for (int i = 0; i < n; i ++) {
			cin >> s[i];
		}

		// 每租都要初始化为0(一定注意,细节问题)
        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(s[1],a); b ++) {
				for (int c = 0; c <= min(s[2],b); c ++) {
					for (int d = 0; d <= min(s[3], c); d ++) {
						for (int e = 0; e <= min(s[4], d); e ++) {
							// 起别名,方便后面使用
							long long &v = f[a][b][c][d][e];
							if (a && a - 1 >= b)
								v += f[a - 1][b][c][d][e];
							if (b && b - 1 >= c)
								v += f[a][b - 1][c][d][e];
							if (c && c - 1 >= d)
								v += f[a][b][c - 1][d][e];
							if (d && d - 1 >= e)
								v += f[a][b][c][d - 1][e];
							if (e)
								v += f[a][b][c][d][e - 1];
						}
					}
				}
			}
		}
		cout << f[s[0]][s[1]][s[2]][s[3]][s[4]] << endl;
		cin >> n;
	}
	return 0;
}
  • 本题的分享就结束了,此题的难点就是想清楚状态表示,状态计算就是固定套路了
    有问题的小伙伴可以发在评论区,记得点赞关注加收藏!

你可能感兴趣的:(算法,动态规划)