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;
}
posted @ 2020-09-05 23:34  洛绫璃  阅读(128)  评论(0编辑  收藏  举报