杨老师的照相排列 (线性DP)

AC通道

杨老师希望给他的班级拍一张合照。

学生们将站成左端对齐的多排,靠后的排站的人数不能少于靠前的排。

例如,12名学生(从后向前)可以排列成每排5,3,3,1人,如下所示:

X X X X X
X X X
X X X
X

同时,杨老师希望同行学生身高从左到右依次降低,同列学生身高从后向前依次降低。

还以12名学生为例,给学生们编号(号码越小代表身高越高)后,按照此规则可进行如下两种安排:

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

现在请你编写一个程序,确定在给定每排人数的情况下,不同安排的数量。

输入格式

输入包含多组测试数据。

每组数据两行,第一行包含一个整数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

这是一个入门DP,大佬口中的入门,对于我来说就是炼狱,也就是一个普通的线性DP

如果用这样的DP来入门,我感觉我一辈子也入不了门qwq

用一个五维数组,没错确实是五维dp[i][j][k][l][r]表示对于的每一行的人数是i, j, k, l, r 的时候的方案数

状态转移可以这样思考:

dp[i][j][k][l][r] 是由
· dp[i - 1][j][k][l][r]在第一行添加一个人构成
· dp[i][j - 1][k][l][r]在第二行添加一个人构成
···

约束条件就是:前一排的人必须要大于等于后一排的人才可行,所以在转移方程中的判断条件是,i > j,也就是说这个位置可以添加一个人,此外还需要满足每一排的人数不能多于指定人数 a[ i ]

关于某一行人数为0的情况,可以让循环从0开始,某一行没有人也当作一个方案数即可

对于题目要求的满足单调的条件,因为每一行每一列都是单调递减,只需要在添加学生的时候按照大到小的顺序添加就一定满足了

#include <bits/stdc++.h>
using namespace std;
const int N = 33;
int K, a[6];
typedef long long ll;
void work(){
	memset(a, 0, sizeof a);
	for (int i = 1; i <= K; i++)scanf("%d", a + i);
	ll dp[a[1] + 1][a[2] + 1][a[3] + 1][a[4] + 1][a[5] + 1];
	memset(dp, 0, sizeof dp);
	dp[0][0][0][0][0] = 1;
	for (int i = 0; i <= a[1]; i++){
		for (int j = 0; j <= a[2]; ++j){
			for (int k = 0; k <= a[3]; ++k){
				for (int l = 0; l <= a[4]; ++l){
					for (int r = 0; r <= a[5]; ++r){
						if(i < a[1])dp[i + 1][j][k][l][r] += dp[i][j][k][l][r];
						if(j < a[2] && i > j)dp[i][j + 1][k][l][r] += dp[i][j][k][l][r];
						if(k < a[3] && j > k)dp[i][j][k + 1][l][r] += dp[i][j][k][l][r];
						if(l < a[4] && k > l)dp[i][j][k][l + 1][r] += dp[i][j][k][l][r];
						if(r < a[5] && l > r)dp[i][j][k][l][r + 1] += dp[i][j][k][l][r];
					}
				}
			}
		}
	}
	printf("%lld\n", dp[a[1]][a[2]][a[3]][a[4]][a[5]]);
}
int main()
{
//	freopen("in.in", "r", stdin);
	while (~scanf("%d", &K) && K){
		work();
	}
	return 0;
}

 

posted @ 2019-09-22 11:20  correct  阅读(252)  评论(0)    收藏  举报