题目大意
一家24小时商店想要招募一些员工,24小时的每一个时段需要的员工数不同。现在有n个人参加招聘,他们给出了一个要求t[i],表示如果招募第i个人,第i个人会从t[i]时刻开始工作8个小时。求商店最少雇佣多少员工?
关于差分约束
由经验(?)得知是差分约束。
现在我们把题目里的0时刻看作1时刻,1时刻看作2时刻以此类推,方便表述。
好的,我们有哪些约束条件呢?
现在我们令need[i]表示i时刻需要多少员工,have[i]表示从第i时刻开始工作的有多少员工,s[i]表示雇佣的1到i时刻开始工作的员工数,则:
s[x]-s[x-1]>=0
s[x]-s[x-1]<=have[i]
当1<=x<=7时,s[x]+(s[24]-s[24-(8-x)])>=need[x],即s[x]-s[16+x]>=need[x]-s[24]
当8<=x<=24时,s[x]-s[x-8]>=need[x]
这些式子没问题吧?(应该吧?)
然后我们就会发现一个很麻烦的式子:s[x]-s[16+x]>=need[x]-s[24],这让我怎么连边啊喂???
等等,既然如此我们不如二分查找s[24](即答案)的值吧,这是显然满足单调性的,如果我雇佣mid个员工是不够的,显然要多雇员点员工。如果够了,就可以考虑裁员啦!
这样子我们就可以轻易建图了(我是按照为了求最短路建的图),然而别忘了这个式子:
s[24]-s[0]>=ans(二分出来的答案)
这个是为什么呢?因为我们如果跑最短路,因为要求的东西是s[24]-s[0],所以要尽量小的是s[0],那么24节点是终点,0节点是起点。如果我们让s[24]初始为0的话,s[0]一定是一个负数。而s[0]又是跑最短路得到的,所以其他可行的s[0]一定比求出来的s[0]要大,也就是最后求得的ans=s[24]-s[0]会小于等于当前的s[24]-s[0]
(QAQ我好蠢啊就上面这一段我理解了2个小时,而且网上的大神都是一脸“这个理所当然”的样子)
那么什么情况下说明这个方案不可行呢?自然是出现负权回路,因为这个时候是求不出最短路的。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=1005;
int T,n,tot,bf;
int a[26],num[26],dis[26],js[26],inq[26];
int h[26],to[N<<1],ne[N<<1],w[N<<1];
int spfa(int lim){
int i,x;
queue<int>q;
for(i=0;i<=24;++i)dis[i]=1e8,js[i]=inq[i]=0;
dis[24]=0,inq[24]=1,q.push(24);
while(!q.empty()){
x=q.front(),q.pop(),inq[x]=0;
for(i=h[x];i!=-1;i=ne[i])
if(dis[x]+w[i]<dis[to[i]]){
dis[to[i]]=dis[x]+w[i];
if(!inq[to[i]]){
inq[to[i]]=1,q.push(to[i]),++js[to[i]];
if(js[to[i]]==25)return 0;
}
}
}
return 1;
}
void add(int x,int y,int z)
{to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=z;}
void build(int lim){
int i;tot=0;for(i=0;i<=24;++i)h[i]=-1;
for(i=1;i<=24;++i)add(i,i-1,0),add(i-1,i,num[i]);
for(i=8;i<=24;++i)add(i,i-8,-a[i]);
for(i=1;i<=7;++i)add(i,16+i,lim-a[i]);
add(24,0,-lim);
}
void work(){
int l=0,r=n,mid,ans=-1;
while(l<=r){
mid=(l+r)>>1,build(mid);
if(spfa(mid))ans=mid,r=mid-1;
else l=mid+1;
}
if(ans==-1)printf("No Solution\n");
else printf("%d\n",ans);
}
int main(){
int i,x;
scanf("%d",&T);
while(T--){
for(i=1;i<=24;++i)scanf("%d",&a[i]),num[i]=0;
scanf("%d",&n);
for(i=1;i<=n;++i)scanf("%d",&x),++num[x+1];
work();
}
return 0;
}