题意:一家每天24小时营业的超市,需要一批出纳员来满足它的需求。超市在每天的不同时段需要不同数目的出纳员(例如,午夜只需一小批,而下午则需要很多)来为顾客提供优质服务,他希望雇佣最少数目的纳员。 现在给定一天里每一小时需要出纳员的最少数量R(0),R(1),...,R(23)。R(0)表示从午夜到凌晨1:00所需要出纳员的最少数目;R(1)表示凌晨1:00到2:00之间需要的;等等。每一天,这些数据都是相同的。有N人申请这项工作,每个申请者i有一个数值ti(0<=ti<=23),为上面提到的开始时刻。也就是说,如果第i个申请者被录用,他(或她)将从ti时刻开始连续工作8小时。计算为满足上述限制需要雇佣的最少出纳员数目、在每一时刻可以有比对应R(i)更多的出纳员在工作。
思路:用差分约束来建模。设num[ i ]为i时刻能够开始工作的人数,x[ i ]为 第 i 时刻实际雇佣的人数,那么x[ I ]<=num[ I ]。
设r[ i ](输入的每一时刻所需的人数)为i时刻至少需要工作的人数,于是有如下关系:
x[ I-7 ]+x[ I-6 ]+x[ I-5 ]+x[ I-4 ]+x[ I-3 ]+x[ I-2 ]+x[ I-1 ]+x[ I ]>=r[ I ]
设s[ I ]=x[ 1 ]+x[ 2 ]…+x[ I ],得到
0<=s[ I ]-s[ I-1 ]<=num[ I ], 0<=I<=23
s[ I ]-s[ I-8 ]>=r[ I ], 8<=I<=23 建立关系
s[ 23 ]+s[ I ]-s[ I+16 ]>=r[ I ], 0<=I<=7 建立可以循环的关系
对于以上的几组不等式,我们采用一种非常笨拙的办法处理这一系列的不等式(其实也是让零乱的式子变得更加整齐,易于处理)。首先我们要明白差分约束系统的应用对象(它通常针对多个二项相减的不等式的)于是我们将上面的所有式子都转化成两项未知项在左边,另外的常数项在右边,且中间用>=连接的式子,即:
s[ I ]-s[ I-1 ]>=0 (0<=I<=23)
s[ I-1 ]-s[ I ]>=-num[ I ] (0<=I<=23)
s[ I ]-s[ I-8 ]>=r[ I ] (8<=I<=23)
s[ I ]-s[ I+16 ]>=r[ I ]-sum (0<=I<= 7)
S[23] - S[-1] >= sum;sum为雇佣的出纳员总数。
要加入最后一条边的原因是: 上述式子虽然有解,但求出的s[23]小于代入里的sum!
这时,显然得到的s[]不满足原来的方程了。不过虽然得到的解不满足原方程组,但这并不代表(1)(2)(3)(4)在s[24]=sum时没有可行解!
此外,值得注意的是,当得到的s[23]>sum时,虽然s[23]不一定是最优解,但把ans置成s[23]后,确实是可行解。
所以为了等价原命题,必须再加上条件:s[23]>=sum,这就是所谓加出来的那条边。
代码里用s[24]表示s[-1]
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;
#define INF 0x3fffffff
#define clr(s,t) memset(s,t,sizeof(s));
#define N 26
int t[N],s[N],first[N],top,T,num,dis[N],cnt[N],used[N];
struct edge{
int y,next,w;
}e[1000];
void add(int x,int y,int w){
e[top].y = y;
e[top].w = w;
e[top].next = first[x];
first[x] = top++;
}
void create(int mid){
int i;
top = 0;
clr(first, -1);
for(i = 1;i<24;i++){
add(i-1,i,0);
add(i,i-1,-s[i]);
}
add(24,0,0);
add(0,24,-s[0]);
for(i = 8;i<24;i++)
add(i-8,i,t[i]);
for(i = 0;i<8;i++)
add(i+16,i,t[i]-mid);
add(24,23,mid);
}
int relax(int x,int y,int w){
if(dis[y]<dis[x]+w){
dis[y] = dis[x]+w;
return 1;
}
return 0;
}
int spfa(int s,int t,int value){
int i,now;
queue<int> q;
for(i = 0;i<=24;i++)
dis[i] = -INF;
dis[s] = 0;
q.push(s);
clr(used, 0);
used[s] = 1;
clr(cnt, 0);
while(!q.empty()){
now = q.front();
used[now] = 0;
q.pop();
for(i = first[now];i!=-1;i=e[i].next) {
if(relax(now,e[i].y,e[i].w) && !used[e[i].y]){
q.push(e[i].y);
used[e[i].y] = 1;
cnt[e[i].y]++;
if(cnt[e[i].y]==25)
return 0;
}
}
}
return dis[t]==value;
}
int main(){
scanf("%d",&T);
while(T--){
int i,j,low,high,mid;
clr(s, 0);
for(i = 0;i<24;i++)
scanf("%d",&t[i]);
scanf("%d",&num);
high = num;
for(j = 1;j<=num;j++){
scanf("%d",&i);
s[i]++;
}
low = 1;
while(low <= high){
mid = (low+high)>>1;
create(mid);
j = spfa(24,23,mid);
if(j)
high = mid-1;
else
low = mid+1;
}
if(low == num+1)
printf("No Solution\n");
else
printf("%d\n",low);
}
return 0;
}