1. 程式人生 > 其它 >UVA11671 矩陣中的符號 Sign of Matrix 題解

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\_ \]

本文來自部落格園,作者:xixike,轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15540751.html