|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
const int N = 30, M = 100;
|
|
|
int n; // n个合格的申请人申请岗位
|
|
|
int r[N]; // 各个时间段需要的人员数量
|
|
|
int num[N]; // 第i个申请人可以从num[i]时刻开始连续工作8小时
|
|
|
int dist[N]; // 最长距离,本题是求“最少需要雇佣”,所以是最长路
|
|
|
int cnt[N]; // 用于判正环(最长路)
|
|
|
bool st[N]; // spfa专用是否在队列中的标识
|
|
|
// 邻接表
|
|
|
int e[M], h[N], idx, w[M], ne[M];
|
|
|
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) {
|
|
|
// 每次清空邻接表
|
|
|
memset(h, -1, sizeof h);
|
|
|
idx = 0;
|
|
|
|
|
|
// s(i):从1点到i点,需要雇佣的人员数量
|
|
|
for (int i = 1; i <= 24; i++) {
|
|
|
add(i - 1, i, 0); // s(i) >= s(i-1) + 0
|
|
|
add(i, i - 1, -num[i]); // s(i-1) >= s(i)-num[i]
|
|
|
}
|
|
|
// s(i) >= s(i-8) + r(i)
|
|
|
for (int i = 8; i <= 24; i++) add(i - 8, i, r[i]);
|
|
|
|
|
|
// s(i)>=s(i+16)−s(24)+r(i)
|
|
|
for (int i = 1; i <= 7; i++) add(i + 16, i, -c + r[i]);
|
|
|
// s24的引入,需要再加两个不等式 s(24)=c
|
|
|
|
|
|
add(0, 24, c);
|
|
|
// s(24)>=c -> s(24) >= c +s(0)
|
|
|
// -> s(24) >= s(0) + c
|
|
|
|
|
|
add(24, 0, -c);
|
|
|
// s(24)<=c -> s(24) <= c +s(0)
|
|
|
// -> s(0) >= s(24) -c
|
|
|
}
|
|
|
|
|
|
// spfa找正环
|
|
|
bool spfa(int c) {
|
|
|
build(c); // 建图
|
|
|
// 每次初始化
|
|
|
memset(st, 0, sizeof st);
|
|
|
memset(cnt, 0, sizeof cnt);
|
|
|
memset(dist, -0x3f, sizeof dist);
|
|
|
queue<int> q;
|
|
|
// 超级源点大法好~
|
|
|
for (int i = 0; i <= 24; i++) {
|
|
|
q.push(i);
|
|
|
st[i] = true;
|
|
|
}
|
|
|
while (q.size()) {
|
|
|
int u = q.front();
|
|
|
q.pop();
|
|
|
st[u] = false;
|
|
|
for (int i = h[u]; ~i; i = ne[i]) {
|
|
|
int v = e[i];
|
|
|
if (dist[v] < dist[u] + w[i]) { // 最长路
|
|
|
dist[v] = dist[u] + w[i];
|
|
|
cnt[v] = cnt[u] + 1;
|
|
|
// 一共24个点,发现正环了则返回true
|
|
|
if (cnt[v] >= 25) return true;
|
|
|
if (!st[v]) {
|
|
|
q.push(v);
|
|
|
st[v] = true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
int T;
|
|
|
scanf("%d", &T);
|
|
|
while (T--) {
|
|
|
// 各个时间段收银员最小需求数量的清单
|
|
|
// 这里为了使用前缀和,向后进行了错一位操作
|
|
|
for (int i = 1; i <= 24; i++) scanf("%d", &r[i]);
|
|
|
scanf("%d", &n); // n个合格的申请人申请岗位
|
|
|
|
|
|
memset(num, 0, sizeof num); // 多组测试数据,所以需要每次清零
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
int t;
|
|
|
scanf("%d", &t);
|
|
|
// 申请人可以从num[t+1]时刻开始连续工作8小时
|
|
|
num[t + 1]++; //++代表这个时段可以干活的人数+1
|
|
|
}
|
|
|
|
|
|
// 二分总人数
|
|
|
int l = 0, r = n;
|
|
|
// 雇佣的人员,最少是0,最多是1000
|
|
|
// 人员雇佣的越多,肯定越能满足用工要求,但成本会高
|
|
|
// 所以,存在单调性,可以二分
|
|
|
while (l < r) {
|
|
|
int mid = (l + r) >> 1;
|
|
|
if (!spfa(mid)) // 如果不等式组有解,向左逼近
|
|
|
r = mid;
|
|
|
else
|
|
|
l = mid + 1; // 无解向右逼近
|
|
|
}
|
|
|
if (spfa(l)) // 如果最终计算出来的结果还是无解,那就是无解
|
|
|
puts("No Solution");
|
|
|
else
|
|
|
printf("%d\n", l); // 输出最小值
|
|
|
}
|
|
|
return 0;
|
|
|
} |