P1129 [ZJOI2007] 矩阵游戏
这道题目是一道经典的二分图最大匹配问题。虽然题目描述的是矩阵操作,但通过分析变换的本质,我们可以将其转化为图论模型。
题目核心分析
1. 交换操作的本质
- 行交换:改变了行与行之间的顺序,但同一行内的元素依然属于这一行。
- 列交换:改变了列与列之间的顺序,但同一列内的元素依然属于这一列。
- 关键结论:无论如何交换行或列,矩阵中任意一个黑色格子 \((i, j)\) 的原始行号 \(i\) 和原始列号 \(j\) 的对应关系是不会改变的。也就是说,如果原本第 \(i\) 行、第 \(j\) 列有一个 \(1\),经过交换后,这个 \(1\) 依然代表着“某一行”和“某一列”的交点。
2. 目标转化
我们要使主对角线全为黑色,即找到 \(n\) 个黑色格子,使得它们分别满足:
- 占据不同的行(第 \(1\) 行到第 \(n\) 行各有一个)。
- 占据不同的列(第 \(1\) 列到第 \(n\) 列各有一个)。
如果我们能从原始矩阵中选出 \(n\) 个黑色的格子,使得这 \(n\) 个格子互不同行且互不同列,那么我们一定可以通过有限次的行交换和列交换,把这 \(n\) 个格子挪到主对角线上。
3. 构建二分图模型
我们可以把矩阵的行和列看作二分图的两个点集:
- 左侧点集:表示矩阵的 \(n\) 个行(\(1 \dots n\))。
- 右侧点集:表示矩阵的 \(n\) 个列(\(1 \dots n\))。
- 连线:如果矩阵中 \((i, j)\) 位置是黑色(\(1\)),则在左侧第 \(i\) 个点和右侧第 \(j\) 个点之间连一条边。
问题转化为:在这个二分图中,是否存在一个大小为 \(n\) 的最大匹配?
算法实现步骤
- 多组数据:注意每组数据开始前要清空邻接表和匹配数组。
- 建图:读入 \(n \times n\) 矩阵,若 \(g[i][j] == 1\),则添加边
add(i, j)。 - 匈牙利算法 (Hungarian Algorithm):对每一个行点进行增广路径寻找。
- 判断结果:如果最大匹配数等于 \(n\),输出
Yes,否则输出No。
C++ 代码实现
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
const int MAXN = 205;
vector<int> adj[MAXN]; // 邻接表
int match[MAXN]; // 记录列匹配的行编号
bool vis[MAXN]; // 记录当前增广路中列是否被访问
int n;
// 匈牙利算法找增广路
bool dfs(int u) {
for (int v : adj[u]) {
if (!vis[v]) {
vis[v] = true;
// 如果列 v 未匹配,或者已匹配的行可以找到新的增广路
if (match[v] == 0 || dfs(match[v])) {
match[v] = u;
return true;
}
}
}
return false;
}
void solve() {
cin >> n;
// 1. 重置数据结构
for (int i = 1; i <= n; i++) {
adj[i].clear();
}
memset(match, 0, sizeof(match));
// 2. 建图
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int val;
cin >> val;
if (val == 1) {
adj[i].push_back(j); // 行 i 与 列 j 有边
}
}
}
// 3. 求最大匹配
int count = 0;
for (int i = 1; i <= n; i++) {
memset(vis, false, sizeof(vis)); // 每次找增广路前重置访问标记
if (dfs(i)) {
count++;
}
}
// 4. 输出结果
if (count == n) cout << "Yes" << endl;
else cout << "No" << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
复杂度分析
- 时间复杂度:匈牙利算法的时间复杂度为 \(O(T \times V \times E)\)。在本题中,\(V = n\),\(E \le n^2\),且 \(n \le 200\),\(T \le 20\)。最大计算量约为 \(20 \times 200 \times 40000 \approx 1.6 \times 10^8\)。虽然看起来很大,但匈牙利算法在稀疏图和实际运行中表现极快,\(200\) 的数据量完全可以轻松通过。
- 空间复杂度:\(O(n^2)\),主要是邻接表存储边的开销。

浙公网安备 33010602011771号