题意:有家24小时营业的超市,现在给出每个小时需要的员工数Ri(i = 0, 1, 2, ..., 23),Ri表示从时刻i开始的一个小时内需要的员工数。现在有N名员工可以上岗,每人都有一个固定的上岗时间,且每个人只会在上岗之后固定工作八个小时,不会换班。求在满足人数要求的前提下,可以安排的最少员工数。可能无解。
思路:真是一个思维好题。
设ai为第i小时实际上岗的人数,Si为前i小时一共上岗的人数,则有 ai = Si - Si-1,ai未知。
设bi为第i小时需要的人数,则bi已知。
设ci为第i小时能够上岗的最大人数,则ci已知。
显然,可以得到下列不等式:
0 <= Si - Si-1 <= ci ,(i = 0, 1,2,...,23)
S(i) - S(i-8) >= bi, (i >= 7)
Si + S23 - S(i + 16) >= bi, (i < 7)
有了上面的不等式组,很显然我们只需要求出 S(23) - S(-1) 的最小值就是答案,方便起见将所有下标向右移一位,得到新的不等式组:
0 <= S(i) - S(i-1) <= ci, (S0 = 0, i = 1, 2, 3,..., 24) (1)
S(i) - S(i - 8) >= bi, (i >= 8) (2)
Si + S24 - S(i + 16) >= bi, (i < 8) (3)
对(3)式移项,得
Si - S(i + 16) >= bi - S24, (i < 8) (4)
其中(1)式和(2)可以直接在图中建立相应的边,但是(4)式中存在一个未知的常量S24,而24显然就是我们的答案。想到二分答案的方法,将答案记为X,则有
Si - S(i + 16) >= bi - X, (i < 8) (5)
此时还需要添加一个限制条件,即 S24 - S0 == X,可表示为下面的形式加入到差分约束系统中。
S24 - X >= S0 (6)
S24 - X <= S0 (7)
好了,到这里这道题目的建图过程完成了,要求的是最小的解,跑最长路即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <cstdlib>
#include <set>
#include <string>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 105;
struct edg{
int v, d, nxt;
}G[maxn];
int pre[maxn], tot, dis[maxn], times[maxn];
bool vis[maxn];
int b[25], c[25];
void add(int u, int v, int w) {
G[tot].v = v;
G[tot].d = w;
G[tot].nxt = pre[u];
pre[u] = tot++;
}
int spfa(int x) {
memset(times, 0, sizeof(times));
memset(vis, 0, sizeof(vis));
for (int i = 0; i < 24; ++i) {
dis[i] = -inf; // 求最长路,初始化成负无穷
}
queue<int> que;
dis[0] = 0;
vis[0] = true;
que.push(0);
while (!que.empty()) {
int u = que.front();
que.pop();
vis[u] = false;
for (int i = pre[u]; ~i; i = G[i].nxt) {
int v = G[i].v, w = G[i].d;
if (dis[u] + w > dis[v]) {
dis[v] = dis[u] + w;
if (!vis[v]) {
vis[v] = true;
if (++times[v] > 25) {
return false;
}
que.push(v);
}
}
}
}
return true;
}
bool jud(int x) {
tot = 0;
memset(pre, -1, sizeof(pre));
for (int i = 1; i <= 24; ++i) {
add(i, i - 1, -c[i]); //S(i) - S(i-1) <= ci
add(i - 1, i, 0); //0 <= S(i) - S(i-1)
if (i >= 8) {
add(i - 8, i, b[i]); //S(i) - S(i - 8) >= bi
} else {
add(i + 16, i, b[i] - x); //Si - S(i + 16) >= bi - X
}
}
add(0, 24, x); //S24 - X >= S0
add(24, 0, -x); //S24 - X <= S0
return spfa(x);
}
int main() {
int t, x, n;
scanf("%d", &t);
while (t--) {
for (int i = 1; i <= 24; ++i) {
scanf("%d", &b[i]);
}
memset(c, 0, sizeof(c));
scanf("%d", &n);
for (int i = 0; i < n; ++i) {
scanf("%d", &x);
++c[x + 1];
}
int l = 0, r = n, f = 0;
while (r - l > 1) {
int mid = (r + l) >> 1;
if (jud(mid)) {
f = 1;
r = mid;
} else {
l = mid;
}
}
if (f) {
printf("%d\n", r);
} else {
puts("No Solution");
}
}
return 0;
}