AcWing 368. 銀河
阿新 • • 發佈:2022-04-02
一、差分約束解法
\(N\) 顆恆星的亮度值總和至少有多大
求最小->求所有下界的最大->最長路 √
求最大->求所有上界的最小->最短路
最長路
\(dist[j] ≥ dist[t] + w[i]\) \(t+w[i]→j\)
\(T=1: A=B => A≥B B≥A\) \(B+0→A A+0→B\)
\(T=2: A<B => B≥A+1\) \(A+1→B\)
\(T=3: A≥B => A≥B\) \(B+0→A\)
\(T=4: A>B => A≥B+1\) \(B+1→A\)
\(T=5: A≤B => B≥A\)
\(spfa\)最長路 - 做完後每個點的距離就是最小值
1 邊是正的 - 存在正環 => 無解
2 有解
必須有絕對值
超級源點(能到所有邊)
\(x[i]≥x[0]+1\)
糖果用棧保證\(spfa\)的時間複雜度\(O(n)\)
二、差分約束程式碼
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N = 100010, M = 300010; //與AcWing 1169. 糖果 這道題一模一樣,連測試用例都一樣 stack<int> q; //有時候換成棧判斷環很快就能 LL dist[N]; bool st[N]; int cnt[N]; int n, m; //表示點數和邊數 //鄰接表 int e[M], h[N], idx, w[M], ne[M]; void add(int a, int b, int c) { e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++; } bool spfa() { //求最長路,所以判斷正環 memset(dist, -0x3f, sizeof dist); //初始化為-0x3f //差分約束從超級源點出發 dist[0] = 0; q.push(0); st[0] = true; while (q.size()) { int t = q.top(); q.pop(); st[t] = false; for (int i = h[t]; ~i; i = ne[i]) { int j = e[i]; if (dist[j] < dist[t] + w[i]) { //求最長路 dist[j] = dist[t] + w[i]; cnt[j] = cnt[t] + 1; //注意多加了超級源點到各各節點的邊 if (cnt[j] >= n + 1) return false; if (!st[j]) { q.push(j); st[j] = true; } } } } return true; } int main() { cin >> n >> m; memset(h, -1, sizeof h); for (int i = 0; i < m; ++i) { int op, a, b; // op為選擇 cin >> op >> a >> b; if (op == 1) /** a == b => (a >= b , b >= a) */ add(a, b, 0), add(b, a, 0); else if (op == 2) /** b >= a + 1 */ add(a, b, 1); else if (op == 3) /** a >= b */ add(b, a, 0); else if (op == 4) /** a >= b + 1 */ add(b, a, 1); else /** b >= a */ add(a, b, 0); } /** xi >= x0 + 1 (每個小朋友都要至少一個糖果)*/ //將所有節點與超級源點x0相連 for (int i = 1; i <= n; ++i) add(0, i, 1); if (!spfa()) puts("-1"); else { LL res = 0; for (int i = 1; i <= n; ++i) res += dist[i]; printf("%lld\n", res); } return 0; }