专栏文章

题解:P5784 [CQOI2008] 矩阵的个数

P5784题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minjau4n
此快照首次捕获于
2025/12/02 03:20
3 个月前
此快照最后确认于
2025/12/02 03:20
3 个月前
查看原文

背景

楼上的有些题解没有具体提到 dp 的状态转移这样优化是对的,以及为什么看似 TLE 的复杂度是对的。所以才诞生了这篇题解。

题意

给定一个 n×3n×3 的矩阵的各行各列之和,求满足条件的矩阵的个数。 1n2001\le n\le 200,每行每列之和均不超过 125125

思路

这是一道技术题。这一类题主要有两种解决方式:
  • 数学,排列组合
  • 计数型 dp。
而我们要发现这题的一个特点:只有 33 列。极有限的行数提示我们使用 dp。因为使用 dp 的条件是状态有限。

朴素 dp

aia_i 表示第 ii 行的和。
fi,c1,c2,c3f_{i,c_1,c_2,c_3} 表示处理:前 ii 行,每列的和分别是 c1,c2,c3c_1,c_2,c_3 的矩阵个数。
那么一个 dp 状态是合法的,当且仅当:c1+c2+c3=i=1naic_1+c_2+c_3=\sum_{i=1}^na_i
枚举在第 ii 行会填入的三个非负整数 si,1,si,2,si,3s_{i,1},s_{i,2},s_{i,3}。要求:si,1+si,2+si,3=ais_{i,1}+s_{i,2}+s_{i,3}=a_i
状态转移方程 fi,c1,c2,c3=fi1,c1si,1,c2si,2,c3si,3f_{i,c_1,c_2,c_3}=\sum f_{i-1,c_1-s_{i,1},c_2-s_{i,2},c_3-s_{i,3}}
初始 f0,0,0,0f_{0,0,0,0}11,其余为 00。 目标 fn,C1,C2,C3f_{n,C_1,C_2,C_3}

对 dp 优化

朴素 dp 复杂度 O(nV6)\mathcal{O}(n\cdot V^6)VV 表示值域),这是远远不能接受的。为了减少复杂度,我们要裁剪冗余状态和转移。
前面谈到 c1+c2+c3c_1+c_2+c_3 为定值,si,1+si,2+si,3s_{i,1}+s_{i,2}+s_{i,3} 也是定值。所以我们在每次枚举 ccss 时有很多是不合法的状态。
因此我们只枚举 c1,c2,si,1,si,2c_1,c_2,s_{i,1},s_{i,2},当 c1,c2,si,1,si,2c_1,c_2,s_{i,1},s_{i,2} 确定时,c3,si,3c_3,s_{i,3} 也随之确定了。
所以令 fi,c1,c2f_{i,c_1,c_2} 表示处理:前 ii 行,每列的和分别是 c1,c2c_1,c_2 的矩阵个数。
状态转移方程 fi,c1,c2=fi1,c1si,1,c2si,2f_{i,c_1,c_2}=\sum f_{i-1,c_1-s_{i,1},c_2-s_{i,2}}
初始 f0,0,0f_{0,0,0}11,其余为 00。 目标 fn,C1,C2f_{n,C_1,C_2}
时间复杂度 O(nV2(ai2))\mathcal{O}(n\cdot V^2\cdot (a_i^2))。 空间复杂度 O(nV2)\mathcal{O}(n\cdot V^2)
看似 TLE,实则这是可以接受的。因为:
n\cdot V^2\cdot(a_i^2) \ &= V^2\cdot (\sum a_i^2) \\ &\le V^2\cdot(\sum a_i)^2 \\ &=V^2\cdot V^2=V^4 \end{aligned}$$ 其中 $V \le 125$,因此 $V^4 \approx 2\times 10^8$ 在可接受范围内,可过此题。 这其实这是一个小 trick,因为 $\sum a_i$ 与 $c_1+c_2+c_3$ 的量级是相等的。 ## Code ```cpp #include <bits/stdc++.h> using namespace std; #define int long long // 需要开 long long const int N = 210, M = 1e17; int n, c[10], a[N], f[N][N][N]; signed main() { cin >> n >> c[1] >> c[2] >> c[3]; int tot = 0; // tot 表示 a 数组的和 for (int i = 1;i <= n;i ++) cin >> a[i], tot += a[i]; if (tot != c[1] + c[2] + c[3]) return cout << 0, 0; // 检查总和是否匹配,不匹配直接输出0 f[0][0][0] = 1; // 初始化 for (int i = 1;i <= n;i ++) { for (int u = 0;u <= c[1];u ++) { for (int v = 0;v <= c[2];v ++) { // 第三列的值由 a[i]-s-t 确定 for (int s = 0;s <= a[i] && s <= u;s ++) { for (int t = 0;t + s <= a[i] && t <= v;t ++) { f[i][u][v] = (f[i][u][v] + f[i - 1][u - s][v - t]) % M;// 状态转移 } } } } } cout << f[n][c[1]][c[2]]; } ``` ### 总结 本题通过**减少状态维度**,利用约束条件优化了动态规划,将原本 $\mathcal{O}(nV^6)$ 的复杂度优化到 $\mathcal{O}(nV^4)$,在给定的数据范围内可以通过。关键在于发现行和与列和的**固定关系**,从而减少需要枚举的状态。 ::::info[AI 使用说明] 此题解在写作完成后使用 DeepSeek 进行了检查和润色。 ::::

评论

0 条评论,欢迎与作者交流。

正在加载评论...