UVA11671 矩阵中的符号 Sign of Matrix 题解
Description
Luogu传送门
Solution
差分约束好题。
看到矩阵上行列加减,不难想到差分约束。
对于矩阵上的一个点 \((i,j)\):
- $a_{i,j} = $
0
:那么 \(x_i + y_j = 0\) - $a_{i, j} = $
+
:那么 \(x_i + y_j \geq 1\) - $a_{i, j} = $
-
:那么 \(x_i + y_j \leq -1\)
由于差分约束算法要求不等式的形式为 \(a - b \leq c\) 或 \(a - b \geq c\),所以我们考虑对 \(y_j\) 取相反数。
那么式子就变成了:
- $a_{i, j} = $
0
: \(x_i - (-y_j) \geq 0\) 且 \(x_i - (-y_j) \leq 0\) - $a_{i, j} = $
+
: \((-y_j) - x_i \leq 1\) - $a_{i, j} = $
-
: \(x_i - (-y_j) \leq -1\)
建图跑个最短路即可,若有负环则无解。
我们跑最短路计算出来的 \(dis\) 数组就表示每一行,每一列达到目标条件所需要的步数(任意一组解),总步数就是把 \(dis\) 数组的值加起来。
由于题目还要求我们求出最优解,我们可以对 \(dis\) 数组整体加上或减去同一个数。
因为是同时加上或减去,所以 \(dis\) 数组内部的不等关系并没有变化,依然是一组解。
那么同时加上多少才能让整个数组的和最小呢?
类似于 糖果传递 那道题,我们取让 \(dis\) 数组同时减去它的中位数即可。
Code
#include
#include
#include
#include
#include
using namespace std;
const int N = 210;
const int M = 1e4 + 10;
int n, Case, ans;
char s[N];
struct node{
int v, w, nxt;
}edge[M << 1];
int head[N], tot;
int dis[N], vis[N], t[N];
inline void add(int x, int y, int z){
edge[++tot] = (node){y, z, head[x]};
head[x] = tot;
}
inline bool spfa(){
queue q;
for(int i = 1; i <= (n << 1); ++i)
q.push(i), dis[i] = 0, vis[i] = t[i] = 1;
while(!q.empty()){
int x = q.front();
q.pop();
vis[x] = 0;
for(int i = head[x]; i; i = edge[i].nxt){
int y = edge[i].v;
if(dis[y] > dis[x] + edge[i].w){
dis[y] = dis[x] + edge[i].w;
if(!vis[y]){
vis[y] = 1;
q.push(y);
if((++t[y]) > 2 * n + 1) return 0;
}
}
}
}
return 1;
}
int main(){
while(scanf("%d", &n) && n != -1){
memset(head, 0, sizeof(head));
ans = tot = 0, Case++;
for(int i = 1; i <= n; ++i){
scanf("%s", s + 1);
for(int j = 1; j <= n; ++j){
if(s[j] == '+') add(j + n, i, -1);
else if(s[j] == '-') add(i, j + n, -1);
else add(i, j + n, 0), add(j + n, i, 0);
}
}
printf("Case %d: ", Case);
if(spfa()){
sort(dis + 1, dis + 1 + (n << 1));
for(int i = 1; i <= (n << 1); ++i)
ans += abs(dis[i] - dis[n]);
printf("%d\n", ans);
}else puts("-1");
}
return 0;
}
\[\_EOF\_
\]