Description
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 <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
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 <int> 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
", ans);
}else puts("-1");
}
return 0;
}
[\_EOF\_
]