Acwing393. 雇佣收银员 题解 差分约束

题目链接:https://www.acwing.com/problem/content/description/395/

解题思路:

差分约束。

为了方便起见,定义第 i 个时间段为 i1:00i:00 这个时间段。

首先,为了方便开一个额外的点,令 Ri 对应为题目中的 R(i+1),即 Ri 表示 i1:00i:00 这个时间段的最小需求人数。即用 Ri 替代 R(i+1) 表示第 i 个时间段人数的需求量。

然后,统计一下每个时间段能够供给的最大人数,用 numi 表示第 i 个时间段能够供给的最大人数。

xi 表示第 i 个时间段需要分配的最小人数,可以得到以下式子:

  • 0xinumi,①
  • xi7+xi6++xiri,②

上述这个式子不具有差分约束的一般性,所以开一个前缀和,定义 Si=j=1ixj,上述不等式组转换为:

  • 0SiSi1numi,对任意 1i24
  • 8i24 时,有
    • SiSi8ri
  • 1i7 时,有
    • Si+S24Si+16ri

将上面这些式子重新梳理一下得:

  1. 对于任意 1i24,有 SiSi1+0
  2. 对于任意 1i24,有 Si1Sinumi
  3. 对于任意 8i24,有 SiSi8+ri
  4. 对于任意 1i7,有 SiSi+16+riS24

显然第 4 种情况还是不具有差分约束的一般性。

但是可以发现 S24N1000,所以可以从 0N枚举 S24 的值,差分约束建图判断是否存在负环。不存在负环的最小的 S24 就是我们所需的答案。

另外,因为 S24 目前是一个固定值,所以我们还可以得到一个等式:

S24=S0+C

其中 C 对应的就是 S24 的固定的值。

所以,还需满足如下两个式子:

  • S24S0+C
  • S0S24C

对应需要额外加两条边。

示例程序:

#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;
}
posted @   quanjun  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示