差分约束 : 雇佣收营员
分析
\(本题令s_i为0\sim i点需要的人数和\)
\(\color{Red}{s_i作为图中的点}\)
\(为了方便使用前缀和,r_i统一往后错一位,于是r_i表示i-1\sim i中需要的人数\)
\(因为24小时是一个环的形式,这里的前缀需要注意:\)
- \(0\sim7小时间值班的人数:s_i+s_{24}-s_{i+16}\)
- \(8\sim 24小时值班的人数:s_i-s_{i-8}\)
约束关系
- \(s_i\ge s_{i-1}\)
- \(s_{i-1}\ge s_i-num[i]\ 表示i-1\sim i最多只能招聘num[i]人.\)
- \(i\le7 : s_i\ge s_{i+16}-s_{24}+r_i\)
- \(i\ge 8:s_i\ge s_{i-8}+r_i\)
\(条件3有3个未知数,所有去枚举s_{24}.\)
代码
#include <bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
inline int lowbit(int x) { return x & (-x); }
#define ll long long
#define pb push_back
#define PII pair<int, int>
#define x first
#define y second
#define inf 0x3f3f3f3f
const int N = 30, M = 100;
int n;
int h[N], e[M], w[M], ne[M], idx;
int r[N], num[N];
int dist[N];
int q[N], cnt[N];
bool st[N];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
void build(int c) { //c为s24, 枚举
memset(h, -1, sizeof h);
idx = 0;
add(0, 24, c), add(24, 0, -c); //s24 <= c s24 >= c;
for (int i = 1; i <= 7; ++i) add(i + 16, i, r[i] - c);
for (int i = 8; i <= 24; ++i) add(i - 8, i, r[i]);
for (int i = 1; i <= 24; ++i) {
add(i, i - 1, -num[i]);
add(i - 1, i, 0);
}
}
bool spfa(int c) {
build(c);
memset(dist, -0x3f, sizeof dist);
memset(cnt, 0, sizeof cnt);
memset(st, 0, sizeof st);
int hh = 0, tt = 1;
dist[0] = 0;
q[0] = 0;
st[0] = true;
while (hh != tt) {
int t = q[hh++];
if (hh == N) hh = 0;
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] >= 25) return false;
if (!st[j]) {
q[tt++] = j;
if (tt == N) tt = 0;
st[j] = true;
}
}
}
}
return true;
}
int main() {
//IO;
int _;
cin >> _;
while (_--) {
for (int i = 1; i <= 24; ++i) cin >> r[i];
cin >> n;
memset(num, 0, sizeof num);
for (int i = 0; i < n; ++i) {
int t;
cin >> t;
num[t + 1]++;
}
bool f = 0;
for (int i = 0; i <= 1000; ++i)
if (spfa(i)) {
cout << i << '\n';
f = 1;
break;
}
if (!f) puts("No Solution");
}
return 0;
}