Luogu P2132 小Z的队伍排列 解题报告
小Z想给班里的同学拍一张合影,为此需要先让大家排好队伍。他希望大家站成
k 排,并规定了每排的人数,保证每一排的人数都不多于后面一排的人数。这时小Z发现队伍看起来还是乱糟糟的,原因是大家的身高互不相同。于是,他希望排头对齐,每位同学都比自己正后方的同学以及排头方向的同学矮。
排完以后,善于思考的小Z还想知道一共有多少种排法。
解题思路:
线性 DP。
首先因为“保证每一排的人数都不多于后面一排的人数”且“每位同学都比自己正后方的同学以及排头方向的同学矮”,所以合法方案中每行每列的身高显然单调。
所以我们可以从高到低考虑每个学生所站的位置,这样搞的话,我们只要用一个
而每次考虑一个学生的时候,为了保证单调性,我们只能将他放在满足人员未满并且
所以定义
接下来,我们来设计状态转移方程,本题中的 无脑设计
目标:
转移:
当
当
(此处为当
代码:
#include <cstring>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <string>
#include <cmath>
#include <stack>
using namespace std;
#define ll long long
#define ri register int
char buf[1 << 20], *p1, *p2;
//#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin), p1 == p2)?EOF: *p1++)
template <typename T> inline void read(T &t) {
ri v = getchar();T f = 1;t = 0;
while (!isdigit(v)) {if (v == '-')f = -1;v = getchar();}
while (isdigit(v)) {t = t * 10 + v - 48;v = getchar();}
t *= f;
}
template <typename T,typename... Args> inline void read(T &t,Args&... args) {
read(t);read(args...);
}
const int INF = 0x3f3f3f3f;
const int N = 31;
ll f[N][N][N][N][N],n;
int a[N];
signed main() {
read(n);
for (int i = 1;i <= n;++i) read(a[i]);
f[0][0][0][0][0] = 1;
for (int a1 = 0;a1 <= a[1];++a1) {
for (int a2 = 0;a2 <= min(a1,a[2]);++a2) {
for (int a3 = 0;a3 <= min(a2,a[3]);++a3) {
for (int a4 = 0;a4 <= min(a3,a[4]);++a4) {
for (int a5 = 0;a5 <= min(a4,a[5]);++a5) {
ll &p = f[a1][a2][a3][a4][a5];//指针储存,传统艺能
if (a1 && a1 > a2) p += f[a1-1][a2][a3][a4][a5];
if (a2 && a2 > a3) p += f[a1][a2-1][a3][a4][a5];
if (a3 && a3 > a4) p += f[a1][a2][a3-1][a4][a5];
if (a4 && a4 > a5) p += f[a1][a2][a3][a4-1][a5];
if (a5) p += f[a1][a2][a3][a4][a5-1];
}
}
}
}
}
printf("%lld\n", f[a[1]][a[2]][a[3]][a[4]][a[5]]);
return 0;
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭