|
|
#include <bits/stdc++.h>
|
|
|
using namespace std;
|
|
|
const int N = 30, M = 100;
|
|
|
int n; // n个合格的申请人申请岗位
|
|
|
int r[N]; // 各个时间段需要的人员数量
|
|
|
int num[N]; // num[i]:i点可以来工作的人数
|
|
|
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]
|
|
|
}
|
|
|
// i>=8 时,s(i) >= s(i-8) + r(i)
|
|
|
for (int i = 8; i <= 24; i++) add(i - 8, i, r[i]);
|
|
|
|
|
|
// 7=>i>=1 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个点,发现正环了则返回false
|
|
|
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小时,++代表这个时段可以干活的人数+1
|
|
|
num[t + 1]++; // 这里使用了偏移量+1存储,为了照顾前缀和同学
|
|
|
}
|
|
|
// 枚举0~1000所有点,找到最小的
|
|
|
bool success = false;
|
|
|
for (int i = 0; i <= 1000; i++) // 看看当前枚举到的i是否使得SPFA有环,有环就是无解,无环就有解
|
|
|
if (!spfa(i)) {
|
|
|
printf("%d\n", i);
|
|
|
success = true;
|
|
|
break;
|
|
|
}
|
|
|
if (!success) puts("No Solution");
|
|
|
}
|
|
|
return 0;
|
|
|
} |