UVA11671 矩陣中的符號 Sign of Matrix 題解
阿新 • • 發佈:2021-11-11
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
- $a_{i, j} = $
+
: \((-y_j) - x_i \leq 1\) - $a_{i, j} = $
-
: \(x_i - (-y_j) \leq -1\)
建圖跑個最短路即可,若有負環則無解。
我們跑最短路計算出來的 \(dis\) 陣列就表示每一行,每一列達到目標條件所需要的步數(任意一組解),總步數就是把 \(dis\) 陣列的值加起來。
由於題目還要求我們求出最優解,我們可以對 \(dis\) 陣列整體加上或減去同一個數。
因為是同時加上或減去,所以 \(dis\) 陣列內部的不等關係並沒有變化,依然是一組解。
那麼同時加上多少才能讓整個陣列的和最小呢?
類似於 糖果傳遞 那道題,我們取讓 \(dis\) 陣列同時減去它的中位數即可。
Code
\[\_EOF\_ \]#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; }
本文來自部落格園,作者:xixike,轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15540751.html