求方案数,考虑 dp
—— 状态设计和边界 ——
题目告诉了一个很显然的性质:
- 每一排从左至右保证高度单调递减
- 每一列从后往前保证高度单调递减
那么可以发现,对于每一行,每一列,一定是按高度顺序插入,并且是连续插入,因为如果不连续,就无法保证单调递减的性质
同时,它给出了另一个性质 : \(N_1 \geqslant N_2 \geqslant \cdots \geqslant N_k\) (\(k\) 排, \(N_i\) 为每排长度)
可以总结出,它总是满足这样一个轮廓:
可以设计状态维护这个东西,考虑 \(k\in[1,5]\) ,
则定义 \(f_{a,b,c,d,e}\) 表示 1 ~ 5 行分别的同学数为 a ~ e 时的方案数。
边界:当没有同学时,显然是一种方案,即 \(f_{0,0,0,0,0} = 1\) .
—— 状态转移方程 ——
考虑当前状态 \(f_{a,b,c,d,e}\) 可以由插入 a ~ e 中任意一个转移过来,但要保证后一排的长度不小于前一排这一前提,则有:
\[f_{a,b,c,d,e} = f_{a,b,c,d,e}~ + \begin{cases} f_{a-1,b,c,d,e}&a>0,a-1\geqslant b \\f_{a,b-1,c,d,e}&b>0,b-1\geqslant c \\f_{a,b,c-1,d,e}&c>0,c-1\geqslant d \\f_{a,b,c,d-1,e}&d>0,d-1\geqslant e \\f_{a,b,c,d,e-1}&e>0 \end{cases}\]—— 阶段和答案 ——
暴力枚举五层的数量,同时注意对于 2 ~ 5 层,要保证长度不大于前一层,取个 min 就可以了。
由于是求累加的方案数,那么答案肯定是出现在最大集合,即 \(f_{N_1,N_2,N_3,N_4,N_5}\)
—— 时间复杂度 ——
数据这么小,没必要算什么复杂度了吧 qwq
#include <bits/stdc++.h>
#define re register int
#define min(x, y) (x < y ? x : y)
using namespace std;
typedef long long ll;
const int N = 31;
int n, h[10];
ll f[N][N][N][N][N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
while (cin >> n, n)
{
memset(f, 0, sizeof f);
memset(h, 0, sizeof h);
for (re i = 1; i <= n; i ++) cin >> h[i];
f[0][0][0][0][0] = 1;
for (re a = 0; a <= h[1]; a ++)
for (re b = 0; b <= min(h[2], a); b ++)
for (re c = 0; c <= min(h[3], b); c ++)
for (re d = 0; d <= min(h[4], c); d ++)
for (re e = 0; e <= min(h[5], d); e ++)
{
ll val = f[a][b][c][d][e];
if (a && b <= a - 1) val += f[a - 1][b][c][d][e];
if (b && c <= b - 1) val += f[a][b - 1][c][d][e];
if (c && d <= c - 1) val += f[a][b][c - 1][d][e];
if (d && e <= d - 1) val += f[a][b][c][d - 1][e];
if (e) val += f[a][b][c][d][e - 1];
f[a][b][c][d][e] = val;
}
cout << f[h[1]][h[2]][h[3]][h[4]][h[5]] << '\n';
}
return 0;
}
标签:Picture,int,Permutations,Youngs,geqslant,re,保证,单调,dp
From: https://www.cnblogs.com/hi-zwjoey/p/17816021.html