题意:有个店铺每天每个钟头都有所需要的人数,现在有n个应聘者,这些应聘者有个规矩,就是从ti开始工作后,然后连续工作8小时,问这个店铺每天运营者,最少需要招聘多少个人就够了?
题解:这个题差分约束条件听好想,主要在于处理24循环,以及最后的三条件处理问题,首先用s[i]来表示从0到第i小时时所需要的人数,然后对于i>=8时,可以写出s[i]-s[i-8]>=r[i],对于1<=i<8时,那么条件是:s[24]-s[i+16]+s[i]>=r[i],还有一个条件是0<=s[i]-s[i-1]<=num[i],这个拆成两个条件是s[i]-s[i-1]>=0,s[i-1]-s[i]>=-num[i],但是s[24]-s[i+16]+s[i]>=r[i]这个条件如何处理呢?可以将s[24]转移到右边,然后我们通过二分枚举这个s[24]就可以得出答案了。
附上代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=25;
const int maxm=4*maxn+50;
struct edge{
int v,w,next;
};
edge edges[maxm];
int head[maxn],tot;
void init()
{
memset(head,-1,sizeof(head));
tot=0;
}
void add_edges(int u,int v,int w)
{
edges[tot].v=v;
edges[tot].w=w;
edges[tot].next=head[u];
head[u]=tot++;
}
int val[maxn],num[maxn];
int dist[maxn],cnt[maxn];
bool vis[maxn];
bool spfa()
{
queue<int>q;
for(int i=0;i<=24;i++){
q.push(i);
cnt[i]=1;dist[i]=0;vis[i]=true;
}
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=false;
for(int i=head[u];~i;i=edges[i].next){
int v=edges[i].v,w=edges[i].w;
if(dist[v]<dist[u]+w){
dist[v]=dist[u]+w;
if(!vis[v]){
vis[v]=true;
q.push(v);
if(++cnt[v]>25){
return false;
}
}
}
}
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
init();
for(int i=1;i<=24;i++){
scanf("%d",&val[i]);
}
int m;
scanf("%d",&m);
memset(num,0,sizeof(num));
for(int i=0;i<m;i++){
int c;
scanf("%d",&c);
num[c+1]++;
}
int lb=-1,ub=m+1;
while(ub-lb>1){
int mid=(lb+ub)>>1;
init();
for(int i=1;i<=24;i++){
add_edges(i-1,i,0);
add_edges(i,i-1,-num[i]);
if(i>=8){
add_edges(i-8,i,val[i]);
}else{
add_edges(i+16,i,val[i]-mid);
}
}
add_edges(0,24,mid);
add_edges(24,0,-mid);
if(spfa()){
ub=mid;
}else{
lb=mid;
}
}
if(ub==m+1){
printf("No Solution\n");
}else{
printf("%d\n",ub);
}
}
return 0;
}