Acwing393. 僱傭收銀員 題解 差分約束

題目鏈接:https://www.acwing.com/problem/content/description/395/

解題思路:

差分約束。

爲了方便起見,定義第 \(i\) 個時間段爲 \(i-1:00\)\(i:00\) 這個時間段。

首先,爲了方便開一個額外的點,令 \(R_i\) 對應爲題目中的 \(R(i+1)\),即 \(R_i\) 表示 \(i-1:00\)\(i:00\) 這個時間段的最小需求人數。即用 \(R_i\) 替代 \(R(i+1)\) 表示第 \(i\) 個時間段人數的需求量。

然後,統計一下每個時間段能夠供給的最大人數,用 \(num_i\) 表示第 \(i\) 個時間段能夠供給的最大人數。

\(x_i\) 表示第 \(i\) 個時間段需要分配的最小人數,可以得到以下式子:

  • \(0 \le x_i \le num_i\),①
  • \(x_{i-7} + x_{i-6} + \cdots + x_i \ge r_i\),②

上述這個式子不具有差分約束的一般性,所以開一個前綴和,定義 \(S_i = \sum\limits_{j=1}^i x_j\),上述不等式組轉換爲:

  • \(0 \le S_i - S_{i-1} \le num_i\),對任意 \(1 \le i \le 24\)
  • \(8 \le i \le 24\) 時,有
    • \(S_i - S_{i-8} \ge r_i\)
  • \(1 \le i \le 7\) 時,有
    • \(S_i + S_{24} - S_{i+16} \ge r_i\)

將上面這些式子重新梳理一下得:

  1. 對於任意 \(1 \le i \le 24\),有 \(S_i \ge S_{i-1} + 0\)
  2. 對於任意 \(1 \le i \le 24\),有 \(S_{i-1} \ge S_i - num_i\)
  3. 對於任意 \(8 \le i \le 24\),有 \(S_i \ge S_{i-8} + r_i\)
  4. 對於任意 \(1 \le i \le 7\),有 \(S_i \ge S_{i+16} + r_i - S_{24}\)

顯然第 \(4\) 種情況還是不具有差分約束的一般性。

但是可以發現 \(S_{24} \le N \le 1000\),所以可以從 \(0\)\(N\)枚舉 \(S_{24}\) 的值,差分約束建圖判斷是否存在負環。不存在負環的最小的 \(S_{24}\) 就是我們所需的答案。

另外,因爲 \(S_{24}\) 目前是一個固定值,所以我們還可以得到一個等式:

\(S_{24} = S_0 + C\)

其中 \(C\) 對應的就是 \(S_{24}\) 的固定的值。

所以,還需滿足如下兩個式子:

  • \(S_{24} \ge S_0 + C\)
  • \(S_0 \ge S_{24} - C\)

對應需要額外加兩條邊。

示例程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 25;
struct Edge {
    int v, w;
};
vector<Edge> g[maxn];

int dis[maxn], cnt[maxn];
bool ins[maxn];
bool spfa() {
    stack<int> stk;
    memset(cnt, 0, sizeof(cnt));
    memset(ins, 0, sizeof(ins));
    memset(dis, -0x3f, sizeof(dis));
    dis[0] = 0;
    stk.push(0);
    while (!stk.empty()) {
        int u = stk.top();
        stk.pop();
        ins[u] = false;
        if (++cnt[u] >= 25)
            return false;
        for (auto e : g[u]) {
            int v = e.v, w = e.w;
            if (dis[v] < dis[u] + w) {
                dis[v] = dis[u] + w;
                if (!ins[v])
                    ins[v] = true, stk.push(v);
            }
        }
    }
    return true;
}

int T, r[maxn], N, num[maxn];

int cal() {
    for (int S24 = 0; S24 <= N; S24++) {
        for (int i = 0; i < 25; i++) g[i].clear();
        for (int i = 1; i <= 24; i++) {
            g[i-1].push_back({i, 0});
            g[i].push_back({i-1, -num[i]});
            if (i >= 8)
                g[i-8].push_back({i, r[i]});
            else
                g[i+16].push_back({i, r[i] - S24});
        }
        g[0].push_back({24, S24});
        g[24].push_back({0, -S24});
        if (spfa())
            return S24;
    }
    return -1;
}

int main() {
    scanf("%d", &T);
    while (T--) {
        for (int i = 1; i <= 24; i++)
            scanf("%d", r + i);
        memset(num, 0, sizeof(num));
        scanf("%d", &N);
        for (int i = 0; i < N; i++) {
            int t;
            scanf("%d", &t);
            t++;
            num[t]++;
        }
        int ans = cal();
        if (ans == -1)
            puts("No Solution");
        else
            printf("%d\n", ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章