POJ 1275 Cashier Employment(差分约束)

Cashier Employment
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 7966 Accepted: 3045

Description

A supermarket in Tehran is open 24 hours a day every day and needs a number of cashiers to fit its need. The supermarket manager has hired you to help him, solve his problem. The problem is that the supermarket needs different number of cashiers at different times of each day (for example, a few cashiers after midnight, and many in the afternoon) to provide good service to its customers, and he wants to hire the least number of cashiers for this job. 

The manager has provided you with the least number of cashiers needed for every one-hour slot of the day. This data is given as R(0), R(1), ..., R(23): R(0) represents the least number of cashiers needed from midnight to 1:00 A.M., R(1) shows this number for duration of 1:00 A.M. to 2:00 A.M., and so on. Note that these numbers are the same every day. There are N qualified applicants for this job. Each applicant i works non-stop once each 24 hours in a shift of exactly 8 hours starting from a specified hour, say ti (0 <= ti <= 23), exactly from the start of the hour mentioned. That is, if the ith applicant is hired, he/she will work starting from ti o'clock sharp for 8 hours. Cashiers do not replace one another and work exactly as scheduled, and there are enough cash registers and counters for those who are hired. 

You are to write a program to read the R(i) 's for i=0..23 and ti 's for i=1..N that are all, non-negative integer numbers and compute the least number of cashiers needed to be employed to meet the mentioned constraints. Note that there can be more cashiers than the least number needed for a specific slot. 

Input

The first line of input is the number of test cases for this problem (at most 20). Each test case starts with 24 integer numbers representing the R(0), R(1), ..., R(23) in one line (R(i) can be at most 1000). Then there is N, number of applicants in another line (0 <= N <= 1000), after which come N lines each containing one ti (0 <= ti <= 23). There are no blank lines between test cases.

Output

For each test case, the output should be written in one line, which is the least number of cashiers needed. 
If there is no solution for the test case, you should write No Solution for that case. 

Sample Input

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

Sample Output

1

Source


题面:有家24小时的超市,这家超市要招工人,而且对于每天的不同的时间内需要不同数目的工人。有N个人前来招聘,他们要求在每天特定的时间上班(输入已给出),而这间超市是实行8小时工作制的,超市老板至少要招多少人。


这个题是小黑书上P306的原题,刚刚开始怎么也想不到如何差分约束,搞了两天终于是有点明了才A了。

题解:

首先设h[i](0<=i<=23)分别是每天第i小时需要的工人数目

s[i](0<=i<=23)分别是每天应聘第i小时开始工作的人的数目

dis[i](0<=i<=23)分别是每天从0小时到第i小时雇佣的总人的数

如果要满足超市每天的要求,那么一定存在以下几条不等式

①那么dis[i] - dis[i-1] >= 0,因为每个小时需要的人数可以有,也可以为0;

②dis[i-1] + t[i] >= s[i],从0小时到第i小时雇佣的总人的数一定小于等于从0小时到第i-1小时雇佣的总人的数与应聘第i小时开始工作的人数之和;

③dis[23] - dis[24] >= ans,即从0小时到第23小时雇佣的总人的数等于ans(一天雇佣的总人数之和),而dis[24]是spfa的起点,开始值为0;

④ k = (i + 8)%24, 如果k<i,那么dis[k] - dis[i] >= h[i];否则dis[k] - dis[i] >= h[i] - ans。因为每个工人每天工作8小时,当0<=i<16时k>i,dis[k] - dis[i] >= h[i]表示从0小时到第k小时雇佣的总人的数大于从0小时到第i小时雇佣的总人的数与第i小时开始上班的人数之和;当16<=i<24时,则是dis[k] - dis[i] + ans >= h[i]的变形,这里就不讲了,自己想一下就能懂。

上面的ans并不是给出的,所以我们可以二分枚举ans找到最后的解,如果没有符合答案的ans那么输出No Solution

以下是代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
using namespace std;
const int maxn = 1e4 + 7;
const int n = 30;
const int inf = 0x3f3f3f3f;
struct node{
    int v, w, next;
} edge[maxn];
int head[n], h[n], s[n], dis[n], in[n], cnt;
bool vis[n];

void init(){/// 初始化
    memset(head, -1, sizeof( head));
    memset(dis, -inf, sizeof( dis));
    memset(in, 0, sizeof( in));
    memset(vis, false, sizeof( vis));
    cnt = 1;
}

void add(int u, int v, int w){ /// 链式前向星建图
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt ++;
}

void Build(int key){ /// 根据不同的ans建图
    init();
    int tt;
    add(24, 0, 0);
    add(0, 24, -s[0]);
    add(24, 23, key);
    for(int i = 1; i < 24; i ++){
        add(i-1, i, 0);
        add(i, i-1, -s[i]);
    }
    for(int i = 0; i < 24; i ++){
        if(i < 8) add(i + 16, i, h[i] - key);
        else add(i-8, i, h[i]);
    }
}

bool spfa(int key){ /// spfa找最长路,可能有正环
    stack<int> S;
    dis[24] = 0;
    in[24] ++;
    vis[24] = true;
    S.push(24);
    while(!S.empty()){
        int temp = S.top();
        S.pop();    
        vis[temp] = false;
        for(int i = head[temp]; i != -1; i = edge[i].next){
            int l = edge[i].v, r = edge[i].w;
            if(dis[l] < dis[temp] + r){
                dis[l] = dis[temp] + r;
                if(!vis[l]){
                    vis[l] = true;
                    if(++ in[l] > n) return false; /// in数组记录入栈的次数,如果大于n,那么有正环
                    S.push(l);
                }
            }
        }
    }
    return dis[23] == key; /// 没有正环且找到最长路时,还得判断dis[23](一天雇佣的总人数)是否等于key(即ans)
}

int main(){
    int t, m, x;
    scanf("%d", &t);
    while(t --){
        for(int i = 0; i < 24; i ++) scanf("%d", &h[i]);
        memset(s, 0, sizeof( s));
        scanf("%d", &m);
        for(int i = 1; i <= m; i ++){
            scanf("%d", &x);
            s[x] ++;
        }
        int l = 0, r = m, ans = inf;
        while(l <= r){ /// 二分找ans
            int mid = (l + r)/2;
            Build(mid);
            if(spfa(mid)) ans = min(mid, ans), r = mid - 1;
            else l = mid + 1;
        }
        if(ans != inf) printf("%d\n", ans);
        else printf("No Solution\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值