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.

111 lines
3.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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;
}