1. 程式人生 > 其它 >AcWing 368. 銀河

AcWing 368. 銀河

題目傳送門

一、差分約束解法

\(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\)

\(A+0→B\)

\(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;
}