POJ1275 Cashier Employment 差分约束(优化 二分)
题面
一家超市要每天24小时营业,为了满足营业需求,需要雇佣一大批收银员。
已知不同时间段需要的收银员数量不同,为了能够雇佣尽可能少的人员,从而减少成本,这家超市的经理请你来帮忙出谋划策。
经理为你提供了一个各个时间段收银员最小需求数量的清单R(0),R(1),R(2),…,R(23)。
R(0)表示午夜00:00到凌晨01:00的最小需求数量,R(1)表示凌晨01:00到凌晨02:00的最小需求数量,以此类推。
一共有N个合格的申请人申请岗位,第 i 个申请人可以从ti时刻开始连续工作8小时。
收银员之间不存在替换,一定会完整地工作8小时,收银台的数量一定足够。
现在给定你收银员的需求清单,请你计算最少需要雇佣多少名收银员。
输入格式
第一行包含一个不超过20的整数,表示测试数据的组数。
对于每组测试数据,第一行包含24个整数,分别表示R(0),R(1),R(2),…,R(23)。
第二行包含整数N。
接下来N行,每行包含一个整数ti。
输出格式
每组数据输出一个结果,每个结果占一行。
如果没有满足需求的安排,输出“No Solution”。
数据范围
0≤R(0)≤1000,
0≤N≤1000,
0≤ti≤23
输入样例:
1
1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
5
0
23
22
1
10
输出样例:
1
题解
看题目, 又是一堆不等式约束, 就想差分约束
老套路 设s[i] 表示 [0, i] 时间段内正在工作和已经工作完的总人数,
考虑到, 差分约束, 要从个入点开始, 也就是-1,
所以我们右移, 将 0~23 小时变为 1~24 小时, 0为入点且s[0] ≡ 0;
然而这是个环, 我们要拆环, 如果从 昨天工作到今天凌晨的不算入 s[1],
所以对 每小时的工作人数, 可这样获得(每个人工作8小时)
i >= 8, s[i] - s[i- 8] >= R[i - 1] ps:时间被我们右移了
i <= 7, s[i] + s[24] - s[24 - 8 + i] >= R[i - 1]
我们要将所有节点(小时)连接起来, 设b[i], 表示 在第i小时(平移过的)有多少人可以开始工作
s[i] - s[i - 1] >= 0
s[i] - s[i - 1] <= b[i]
s[24] - s[0] <= n
s[24] - s[0] >= 0
整理得
s[0] = 0
s[i] >= s[i- 1]
s[i - 1] >= s[i] - b[i]
i >= 8, s[i] >= s[i - 8] + R[i - 1]
i <= 7, s[i] >= s[16 + i] - s[24] + R[i - 1]
s[0] >= s[24] - n
s[24] >= s[0]
问题来了, (1)对于 i<= 7 的不等式, 怎么和两个点有关
再仔细一看, s[24]正是我们所求, 且s[24]是个与i无关的数, 即"常数"
所以我们可以枚举 s[24] 为 s24, 然后使得 s[24] - s[0] <= s24, s[24] - s[0] >= s24, 强制约束
由于求最小值, 从小到大枚举即可,
思考的, s[24]是单调的, 那么就可以二分
然后就是建边问题, 我们按照
s[i] >= s[i- 1]
s[i - 1] >= s[i] - b[i]
i >= 8, s[i] >= s[i - 8] + R[i - 1]
i <= 7, s[i] >= s[16 + i] -+ R[i - 1]
s[0] >= s[24]
s[24] >= s[0]
对于 边的序号 > 65的边 在搜索时取花费时额外 减去当前枚举的 s24, 并且 左后一条边的花费预先 加上 s24 * 2(取的时候 -s24)
#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef pair<ll, ll> PLL;
typedef vector<int> VI;
typedef double db;
const int N = 25;
int n, m, _, k;
int h[N], ne[3 * N], to[3 * N], co[3 * N], tot;
int a[N], b[N], dis[N], dep[N];
bool v[N];
void add(int u, int v, int c) {
ne[++tot] = h[u]; to[h[u] = tot] = v; co[tot] = c;
}
bool sfpa(int mid) {
stack<int> st; st.push(0);
rep(i, 0, 24) v[i] = 0;
co[tot] = (mid << 1);
memset(dis, -0x3f, sizeof dis);
dep[0] = dis[0] = 0;
while (!st.empty()) {
int x = st.top(); st.pop(); v[x] = 0;
for (int i = h[x]; i; i = ne[i]) {
int y = to[i], c = co[i] - (i > 65) * mid;
if (dis[y] >= dis[x] + c) continue;
dis[y] = dis[x] + c; dep[y] = dep[x] + 1;
if (dep[y] >= 25) return 0;
if (!v[y]) v[y] = 1, st.push(y);
}
}
return 1;
}
int main() {
IOS;
for (cin >> _; _; --_) {
h[0] = tot = 0;
rep(i, 1, 24) h[i] = b[i] = 0, cin >> a[i];
cin >> n;
rep(i, 1, n) cin >> m, ++b[++m];
rep(i, 1, 24) add(i, i - 1, -b[i]), add(i - 1, i, 0);
rep(i, 8, 24) add(i - 8, i, a[i]);
rep(i, 1, 7) add(i + 16, i, a[i]);
add(24, 0, 0); add(0, 24, 0);
int l = 0, r = n + 1;
while (l < r) {
int mid = (l + r) >> 1;
if (sfpa(mid)) r = mid;
else l = mid + 1;
}
if (r == n + 1) cout << "No Solution\n";
else cout << r << '\n';
}
return 0;
}