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 <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\n", ans);
        }else puts("-1");
    }
    return 0;
}

\[\_EOF\_ \]

posted @ 2021-11-11 17:10  xixike  阅读(80)  评论(0编辑  收藏  举报