SP15637 GNYR04H - Mr Youngs Picture Permutations(线性 dp)

发布时间 2023-11-07 21:11:01作者: Zhang_Wenjie

题目

求方案数,考虑 dp

—— 状态设计和边界 ——

题目告诉了一个很显然的性质:

  • 每一排从左至右保证高度单调递减
  • 每一列从后往前保证高度单调递减

那么可以发现,对于每一行,每一列,一定是按高度顺序插入,并且是连续插入,因为如果不连续,就无法保证单调递减的性质

同时,它给出了另一个性质 : \(N_1 \geqslant N_2 \geqslant \cdots \geqslant N_k\)\(k\) 排, \(N_i\) 为每排长度)

可以总结出,它总是满足这样一个轮廓:

image

可以设计状态维护这个东西,考虑 \(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;
}