UVa 11671 Sign of a Matrix
题目分析
本题要求我们通过最少的操作次数,将一个初始全为 000 的 n×nn \times nn×n 矩阵转换为目标符号矩阵。
操作定义
每次操作可以选择:
- 将某一整行的所有元素同时 +1+1+1 或 −1-1−1
- 将某一整列的所有元素同时 +1+1+1 或 −1-1−1
符号约束
目标矩阵中每个位置的符号有三种可能:
+表示该位置最终值 >0> 0>0-表示该位置最终值 <0< 0<00表示该位置最终值 =0= 0=0
关键观察
设第 iii 行进行的净操作次数为 RiR_iRi(加为正,减为负),第 jjj 列进行的净操作次数为 CjC_jCj,则矩阵中 (i,j)(i, j)(i,j) 位置的最终值为:
Ai,j=Ri+Cj A_{i,j} = R_i + C_j Ai,j=Ri+Cj
因此,问题转化为:
- 寻找整数序列 R1,R2,…,RnR_1, R_2, \dots, R_nR1,R2,…,Rn 和 C1,C2,…,CnC_1, C_2, \dots, C_nC1,C2,…,Cn
- 满足所有符号约束
- 最小化 ∑i=1n∣Ri∣+∑j=1n∣Cj∣\sum_{i=1}^{n} |R_i| + \sum_{j=1}^{n} |C_j|∑i=1n∣Ri∣+∑j=1n∣Cj∣
解题思路
1. 约束条件的形式化
对于每个位置 (i,j)(i, j)(i,j),根据目标符号有:
- 若为
+:Ri+Cj≥1R_i + C_j \ge 1Ri+Cj≥1 - 若为
-:Ri+Cj≤−1R_i + C_j \le -1Ri+Cj≤−1 - 若为
0:Ri+Cj=0R_i + C_j = 0Ri+Cj=0
2. 变量代换与差分约束转化
这是本题最精妙的一步。我们引入新的变量表示,令:
- Xi=RiX_i = R_iXi=Ri,其中 0≤i<n0 \le i < n0≤i<n
- Xn+j=−CjX_{n+j} = -C_jXn+j=−Cj,其中 0≤j<n0 \le j < n0≤j<n
则原有的约束可以统一改写为:
对于 + 约束:
Ri+Cj≥1⇒Ri−(−Cj)≥1⇒Xn+j−Xi≤−1 R_i + C_j \ge 1 \Rightarrow R_i - (-C_j) \ge 1 \Rightarrow X_{n+j} - X_i \le -1 Ri+Cj≥1⇒Ri−(−Cj)≥1⇒Xn+j−Xi≤−1
对于 - 约束:
Ri+Cj≤−1⇒Ri−(−Cj)≤−1⇒Xi−Xn+j≤−1 R_i + C_j \le -1 \Rightarrow R_i - (-C_j) \le -1 \Rightarrow X_i - X_{n+j} \le -1 Ri+Cj≤−1⇒Ri−(−Cj)≤−1⇒Xi−Xn+j≤−1
对于 0 约束:
Ri+Cj=0⇒Ri−(−Cj)=0⇒{Xi−Xn+j≤0Xn+j−Xi≤0 R_i + C_j = 0 \Rightarrow R_i - (-C_j) = 0 \Rightarrow \begin{cases} X_i - X_{n+j} \le 0 \\ X_{n+j} - X_i \le 0 \end{cases} Ri+Cj=0⇒Ri−(−Cj)=0⇒{Xi−Xn+j≤0Xn+j−Xi≤0
所有约束都转化为了标准形式 Xv−Xu≤wX_v - X_u \le wXv−Xu≤w,这正是差分约束系统的标准形式。
3. 图论建模
将每个变量 XkX_kXk 视为图中的一个节点(共 2n2n2n 个节点),对于每条约束 Xv−Xu≤wX_v - X_u \le wXv−Xu≤w,添加一条从 uuu 到 vvv 的边,权值为 www。
具体建图规则:
+:添加边 n+j→in+j \to in+j→i,权值为 −1-1−1-:添加边 i→n+ji \to n+ji→n+j,权值为 −1-1−10:添加边 i→n+ji \to n+ji→n+j 和 n+j→in+j \to in+j→i,权值均为 000
4. 求解差分约束系统
使用 SPFA\texttt{SPFA}SPFA 算法求解最短路:
- 将所有节点的初始距离设为 000,并全部加入队列
- 运行 SPFA\texttt{SPFA}SPFA,同时记录每个节点的入队次数
- 如果某个节点入队次数达到 2n2n2n 次,说明图中存在负环,约束系统无解,输出 −1-1−1
- 否则,distdistdist 数组保存了一组可行解,即每个 XkX_kXk 的一个可行取值
为什么初始距离设为 000 且全部入队?
- 差分约束系统需要添加超级源点,但我们可以等价地将所有点初始距离设为 000 并入队,效果相同且更简洁
- 这样可以找到一组使得所有变量尽可能接近 000 的解
5. 还原变量并最小化操作次数
得到 XXX 数组后,还原原始变量:
- Ri=XiR_i = X_iRi=Xi
- Cj=−Xn+jC_j = -X_{n+j}Cj=−Xn+j
由于差分约束系统只确定了变量之间的相对关系,我们可以给所有 RiR_iRi 加上一个偏移量 shiftshiftshift:
- Ri′=Ri+shiftR_i^{'} = R_i + shiftRi′=Ri+shift
- 由于 Xn+j=−CjX_{n+j} = -C_jXn+j=−Cj 也包含了相同的相对关系,对应地 Cj′=Cj−shiftC_j^{'} = C_j - shiftCj′=Cj−shift
操作总次数为:
∑i=0n−1∣Ri+shift∣+∑j=0n−1∣Cj−shift∣ \sum_{i=0}^{n-1} |R_i + shift| + \sum_{j=0}^{n-1} |C_j - shift| i=0∑n−1∣Ri+shift∣+j=0∑n−1∣Cj−shift∣
代入 Cj=−Xn+jC_j = -X_{n+j}Cj=−Xn+j,得:
∑i=0n−1∣Xi+shift∣+∑j=0n−1∣−Xn+j−shift∣ \sum_{i=0}^{n-1} |X_i + shift| + \sum_{j=0}^{n-1} |-X_{n+j} - shift| i=0∑n−1∣Xi+shift∣+j=0∑n−1∣−Xn+j−shift∣
我们只需在 shift∈[−n,n]shift \in [-n, n]shift∈[−n,n] 范围内枚举,计算每种情况下的总操作次数,取最小值即可。
为什么枚举范围是 [−n,n][-n, n][−n,n]?
- 由于每个变量的值不会偏离 000 太远(否则操作次数会很大),在 n≤100n \le 100n≤100 时,[−n,n][-n, n][−n,n] 的范围已经足够覆盖最优解
6. 时间复杂度分析
- 建图:O(n2)O(n^2)O(n2)
- SPFA\texttt{SPFA}SPFA 算法:平均时间复杂度 O(kE)O(kE)O(kE),其中 E=O(n2)E = O(n^2)E=O(n2),kkk 为常数
- 枚举平移量:O(n2)O(n^2)O(n2)
总体时间复杂度 O(n2)O(n^2)O(n2),在 n≤100n \le 100n≤100 时可以在毫秒级完成所有测试用例。
参考代码
// Sign of a Matrix
// UVa ID: 11671
// Verdict: Accepted
// Submission Date: 2026-04-12
// UVa Run Time: 0.010s
//
// 版权所有(C)2026,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
const int INF = 1e9;
const int MAXN = 205;
struct Edge {
int to, weight;
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, caseNum = 0;
while (cin >> n && n != -1) {
vector<string> grid(n);
for (int i = 0; i < n; ++i) cin >> grid[i];
int total = 2 * n;
vector<vector<Edge>> adj(total);
// 根据符号建图
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j) {
int u = i; // R[i]
int v = n + j; // -C[j]
if (grid[i][j] == '+')
adj[v].push_back({u, -1}); // -C[j] - R[i] <= -1
else if (grid[i][j] == '-')
adj[u].push_back({v, -1}); // R[i] - (-C[j]) <= -1
else { // '0'
adj[u].push_back({v, 0}); // R[i] - (-C[j]) <= 0
adj[v].push_back({u, 0}); // -C[j] - R[i] <= 0
}
}
// SPFA 求最短路并检测负环
vector<int> dist(total, 0); // 所有点初始距离为 0
vector<int> cnt(total, 0);
vector<bool> inQueue(total, true);
queue<int> q;
for (int i = 0; i < total; ++i) q.push(i);
bool hasNegCycle = false;
while (!q.empty() && !hasNegCycle) {
int u = q.front();
q.pop();
inQueue[u] = false;
for (const Edge& e : adj[u]) {
int v = e.to;
int w = e.weight;
if (dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
cnt[v] = cnt[u] + 1;
if (cnt[v] >= total) {
hasNegCycle = true;
break;
}
if (!inQueue[v]) {
q.push(v);
inQueue[v] = true;
}
}
}
}
cout << "Case " << ++caseNum << ": ";
if (hasNegCycle) {
cout << -1 << "\n";
continue;
}
// 计算最小操作次数:枚举平移量
int ans = INF;
for (int shift = -n; shift <= n; ++shift) {
int sum = 0;
for (int i = 0; i < n; ++i)
sum += abs(dist[i] + shift); // R[i]
for (int j = 0; j < n; ++j)
sum += abs(-dist[n + j] - shift); // C[j] = -(-C[j]) 的相反数
ans = min(ans, sum);
}
cout << ans << "\n";
}
return 0;
}
总结
本题是一道经典的差分约束系统应用题,核心难点在于:
- 问题转化:将矩阵操作转化为行变量和列变量的线性组合
- 变量代换:通过令 Xn+j=−CjX_{n+j} = -C_jXn+j=−Cj,将所有约束统一为 Xv−Xu≤wX_v - X_u \le wXv−Xu≤w 的形式
- 图论建模:将差分约束转化为有向图中的边,使用 SPFA\texttt{SPFA}SPFA 算法求解
- 最优化处理:通过枚举平移量找到使操作次数最小的解
掌握这种将代数约束转化为图论模型的方法,对于解决许多组合优化问题都有很大帮助。差分约束系统不仅适用于本题,在求解诸如区间约束、任务调度等问题时也有广泛应用。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)