题目大意
一个店在不同的时间需要不同数量的店员.
现在又n个店员来应聘,如果聘用一个店员,那么他会工作8个小时.
每个应聘者都有一个工作开始时间.
问最少雇佣几个应聘者.
题解
这是一道论文题.
2006 冯威的<数与图的完美结合——-浅析差分约束系统>
首先定义一堆的数组.
定义r[i]表示在i时店里需要r[i]个店员.
x[i]表示在i时店长可以雇佣x[i]个店员
t[i]表示在i时店长实际雇佣了t[i]个店员
差分约束系统的关键就是找到大小关系,那么可以挖掘题目信息来推一推.
我在推导时习惯化成
⩾
由于一个时间点可以雇佣的店员一定,那么
t[i]⩽x[i]
由于一个时间点在店中的店员必须大于r[i]
那么对于 i⩾8
∑j=i−7it[j]⩾r[i]
对于
i<8
,则有
∑j=0j⩽it[j]+∑j=i+1623t[j]⩾r[i]
同时,一个时间点的雇佣人数不可以为负,所以
t[i]⩾0
显然,这些都可以用前缀和维护. 令s[i]表示t[i]的前缀和. 于是这一堆得
Σ
就变成了:
s[i−1]−s[i]⩾−x[i]s[i]−s[i−8]⩾r[i] i⩾8s[i]−s[i+16]⩾r[i]−s[23] i<8s[i]−s[i−1]⩾0
但是 s[23] 并不是一个常量,这就给我们带来了不少麻烦.
由于数据范围比较小,可以考虑枚举 s[23] ,然后再判断是不是可行.
于是每次重构第三条式子所代表的边.由于每次要清零,所以正向表比较方便.
构完图之后跑一边最长路,如果出现负圈等等一些条件就是不满足.
s[23]的取值明显满足二分性质,可以改成二分查找.
#include <cstdio>
#include <algorithm>
using namespace std;
const int M=1005;
const int INF=2000000000;
int head[M],etot,r[M],s[M],x[M],Q[M*40];
struct Edge{
int to,v,nxt;
Edge(int _to=0,int _v=0,int _nxt=0):to(_to),v(_v),nxt(_nxt){}
}edge[M<<2];
void add_edge(int a,int b,int c){
edge[etot]=Edge(b,c,head[a]);
head[a]=etot++;
}
int cnt[M],tmphead[M],n;
bool mark[M];
bool SPFA(){
for(int i=0;i<=24;i++)
s[i]=-INF,cnt[i]=mark[i]=0;
s[24]=0;
int L=0,R=0;
Q[R++]=24;
while(L<R){
int x=Q[L++];
mark[x]=0;
for(int i=head[x];~i;i=edge[i].nxt){
int to=edge[i].to;
if(s[to]<s[x]+edge[i].v){
s[to]=s[x]+edge[i].v;
cnt[to]++;
if(cnt[to]==n+1) return 0;//负圈
if(!mark[to]){
mark[to]=1;
Q[R++]=to;
}
}
}
}
return 1;
}
void solve(){
for(int i=0;i<24;i++)
scanf("%d",&r[i]);
scanf("%d",&n);
for(int i=0;i<25;i++)
head[i]=-1;
for(int i=0;i<n;i++){
int a;
scanf("%d",&a);
x[a]++;
}
etot=0;
for(int i=8;i<24;i++)//s(i)-s(i-8)>=r(i)
add_edge(i,i-8,r[i]);
//24是起点
add_edge(24,0,-x[0]);
add_edge(0,24,0);
for(int i=1;i<24;i++){
add_edge(i-1,i,-x[i]);//s(i-1)-s(i)>=x(i)
add_edge(i,i-1,0);//x(i)-x(i-1)>=0
}
int tmp=etot,ans=-1;
for(int i=0;i<=24;i++)
tmphead[i]=head[i];
int L=0,R=n;
while(L<=R){//枚举s[23]
int i=(L+R)>>1;
for(int j=0;j<8;j++)
add_edge(j,j+16,r[j]-i);
add_edge(23,24,i);//这很重要,s[23]>=i
if(SPFA()){
bool f=1;
for(int j=1;j<24;j++){
if(s[j-1]-s[j]>x[j]){
f=0;
break;
}
}
if(f) R=i-1,ans=i;
else L=i+1;
}else L=i+1;
//还原图
etot=tmp;
for(int j=0;j<=24;j++)
head[j]=tmphead[j];
}
if(~ans) printf("%d\n",ans);
else puts("No Solution");
}
int main(){
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}