POJ 1275 - Cashier Employment
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
Solution:
这题是比较难想的(而且难调的)差分约束的题。。。
简单讲一下题意:在一家超市里,每个时刻都需要有营业员看管,R(i) (0 <= i < 24)表示从i时刻开始到i+1时刻结束需要的营业员的数目,现在有N(N <= 1000)个申请人申请这项工作,并且每个申请者都有一个起始工作时间 ti,如果第i个申请者被录用,那么他会从ti时刻开始连续工作8小时。现在要求选择一些申请者进行录用,使得任何一个时刻i,营业员数目都能大于等于R(i)。求出至少需要录用多少营业员。
我们可以转换一下思维:
首先,由于每天的工作情况都是一样的,所以我们只需要求出一天的情况即可。
再根据题意,令$s[i]$为一天内前$i+1$个小时录用的人数,分析:
1、如果$i>=7$,则$s[i] - s[i-8] \geq R[i]$;
2、如果$0 \leq i < 7$,则可以推出$s[23] - s[i+16] + s[i] \geq R[i]$;
3、同时每个时刻录用的人数有个上限,假设第$i$时刻最多可以录用的人数为$b[i]$,则对于每一个$i$有$0 \leq s[i] - s[i-1] \leq b[i]$。
现在需要解决的一个问题是,第二个不等式中包含$3$个$s$组的变量,不能去线性组合了,那么如何建图呢?
既然有三个变量,而总的答案不会太大,所以可以只需要枚举$s[23]$,那么这个量就是已知的了,因此不等式可以变为$s[i] - s[i+16]\geq R[i] - s[23]$,但是必须明白的一点是,既然$s[23]$是枚举的一天的录用人数的最小数目,我们建图之后求出的$s[23]$也应该为枚举的那个值,可以从$0$到$n$枚举$s[23]$,第一个值便是答案,但是可以更高效地求解,因为问题具有单调性,直接怼二分.
最后,因为s$[-1] = 0$,数组越界无法表示,所以需要整体向右移一位即可,那么令$s[0] = 0$。
思路就很清晰了,二分$s[24]$求满足条件的最小值,若不存在则输出无解。
代码:
#include<bits/stdc++.h> #define il inline #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) using namespace std; const int N=2005,M=26,inf=23333333; int T,n,a[M],dis[M],tot[M],net[N],to[N],cnt,h[M],w[N]; bool vis[M]; queue<int>q; il int gi(){ int a=0;char x=getchar(); while(x<'0'||x>'9')x=getchar(); while(x>='0'&&x<='9')a=(a<<3)+(a<<1)+x-48,x=getchar(); return a; } il void add(int u,int v,int c){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt,w[cnt]=c;} il void init(int x){ cnt=0; memset(vis,0,sizeof(vis)); memset(h,0,sizeof(h)); memset(dis,-0x3f,sizeof(dis)); while(!q.empty())q.pop(); For(i,0,23) add(i,i+1,0),add(i+1,i,-tot[i]); For(i,7,23) add(i-7,i+1,a[i]); add(0,24,x),add(24,0,-x); For(i,0,6) add(i+17,i+1,a[i]-x); } il bool check(int x){ init(x); q.push(0);dis[0]=0; while(!q.empty()){ int u=q.front();q.pop();vis[u]=0; if(u==24&&dis[u]>x)return 0; for(int i=h[u];i;i=net[i]) if(dis[to[i]]<dis[u]+w[i]){ dis[to[i]]=dis[u]+w[i]; if(!vis[to[i]])q.push(to[i]),vis[to[i]]=1; } } return dis[24]==x?1:0; } il void solve(){ int l=0,r=n+1,mid,ans=inf; while(l<=r){ mid=l+r>>1; if(check(mid))r=mid-1,ans=mid; else l=mid+1; } if(ans>n)printf("No Solution\n"); else printf("%d\n",ans); } int main(){ T=gi(); while(T--){ memset(tot,0,sizeof(tot)); For(i,0,23) a[i]=gi(); n=gi(); int u; For(i,1,n)u=gi(),tot[u]++; solve(); } return 0; }