Files
python/TangDou/AcWing/ChaFenYueShu/393_ErFen.cpp
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

111 lines
3.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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