​Acwing 271. 杨老师的照相排列​

题目

有 n 个人,编号 1 ~ n。要站成 k 排,现在给出每排的人数,保证从前往后不递增。现在给出总人数,求符合要求的总方案数。

分析

​https://www.bilibili.com/video/av69678938​

根据闫氏 dp 分析法,做 dp 先要划分集合,弄清除每个集合代表的意义。

Acwing 271. 杨老师的照相排列(线性dp求方案数)_c++ 代表 5 排人数分别为 a, b, c, d, e,时的方案数,这时 Acwing 271. 杨老师的照相排列(线性dp求方案数)_#include_02

进而得出状态转移方程。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f;
const int N = 31;

int k, s[5];
ll dp[N][N][N][N][N];

int main() {
while (scanf("%d", &k), k) {
memset(s, 0, sizeof(s));
memset(dp, 0, sizeof(dp));
for (int i = 0; i < k; 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 <= 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++) {
ll &v = dp[a][b][c][d][e];
if (a && a - 1 >= b) v += dp[a-1][b][c][d][e];
if (b && b - 1 >= c) v += dp[a][b-1][c][d][e];
if (c && c - 1 >= d) v += dp[a][b][c-1][d][e];
if (d && d - 1 >= e) v += dp[a][b][c][d-1][e];
if (e) v += dp[a][b][c][d][e-1];
}
printf("%lld\n", dp[s[0]][s[1]][s[2]][s[3]][s[4]]);
}
return 0;
}