POJ 1275 Cashier Employment(差分约束系统+二分)

本文介绍了一种解决员工调度问题的算法,通过建立有向图并使用SPFA算法寻找最短路径来确定最少雇佣人数,适用于店铺按时间段需求调整员工数量的情况。

题意:一家店给出每个时间段(0:00 - 23:00)需要的员工数,再给出n个员工的申请雇佣时间段,每一个员工可以连续工作8小时,问一天最少需要雇佣多少员工

思路:设di : 开始到第i时间刻雇佣的人数一共多少人,则有:

0 <= d[i] - d[i - 1] <= t[i]

d[i] - d[i - 8] >= R[i]  (i >= 8 时)

d[i] - d[i + 16] >= R[i] - answer (i < 8时, 相当于增加一天)

d[24] - d[0] >= answer

根据这些式子建立有向图,看是否存在d[24] 到 d[0]的最短路,二分求answer

#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<iostream>
#include<algorithm>
#define bug printf("CCC\n")
const int maxn = 60;
const int INF = 1e9;
typedef long long ll;
using namespace std;

struct P {
    int to, cost;
    P() {}
    P(int t, int c) : to(t), cost(c) {}
};
int vis[maxn], cnt[maxn], d[maxn];
int peo[maxn], r[maxn];
int T, n, p, f;
vector<P> G[maxn];

void init() {
    for(int i = 0; i < maxn; i++)
        G[i].clear();
    memset(peo, 0, sizeof(peo));
}

void build(int ans) {
    for(int i = 1; i <= 24; i++) {
        int from = i, to = i - 1;
        G[to].push_back(P(from, peo[i]));
        G[from].push_back(P(to, 0));
    }
    for(int i = 1; i <= 24; i++) {
        if(i < 8) G[i].push_back(P(i + 16, ans - r[i]));
        else G[i].push_back(P(i - 8, -r[i]));
    }
    G[24].push_back(P(0, -ans));
}

bool spfa(int from) {
    fill(d, d + maxn, INF);
    memset(vis, 0, sizeof(vis));
    memset(cnt, 0, sizeof(cnt));
    d[from] = 0; vis[from] = 1;
    queue<int> q; q.push(from);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        vis[u] = 0;
        for(int i = 0; i < G[u].size(); i++) {
            P st = G[u][i];
            int v = st.to;
            if(d[v] > d[u] + st.cost) {
                d[v] = d[u] + st.cost;
                if(vis[v]) continue;
                q.push(v);
                if(++cnt[v] > 24) return true;
            }
        }
    }
    return false;
}

int bsearch(int l, int r) {
    while(l < r) {
        for(int i = 0; i < maxn; i++)
            G[i].clear();
        int mid = (l + r) >> 1;
        build(mid);
        bool circle = spfa(24);
        if(circle) l = mid + 1;
        else r = mid;
    }
    return r;
}

int main() {
    scanf("%d", &T);
    while(T--) {
        init();
        int sum = 0, sum1 = 0;
        for(int i = 1; i <= 24; i++) {
            scanf("%d", &r[i]);
            sum += r[i];
        }
        scanf("%d", &n);
        while(n--) {
            scanf("%d", &p);
            peo[++p]++;
            sum1++;
        }
        sum = min(sum1, sum);
        int ans = bsearch(0, sum + 1);
        if(ans > sum) printf("No Solution\n");
        else printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值