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 \le i \le 24\),有 \(S_i \ge S_{i-1} + 0\)
- 对于任意 \(1 \le i \le 24\),有 \(S_{i-1} \ge S_i - num_i\)
- 对于任意 \(8 \le i \le 24\),有 \(S_i \ge S_{i-8} + r_i\)
- 对于任意 \(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;
}