Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 7966 | Accepted: 3045 |
Description
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
Output
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;
}