You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
3.3 KiB

2 years ago
#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;
}