AcWing 393. 雇佣收银员

题目大意

一个超市需要收银员,各个时间段需要的最小数量为r[i],有n个人申请岗位,开始工作的时间为t[i],问最少需要多少收银员?

解题思路

可以考虑差分约束,
数组r[i]表示超市在i-1时到i时需要的收银员数量;
num[i]表示i-1~i时可以工作的收银员数量
s[i]表示i-1~i时开始工作的收银员人数
sum[i]表示0~i时所用的收银员人数(即s[]的前缀和数组)
对于i 满足1<=i<=24;

1) num[i]>=sum[i]-sum[i-1]>=0

含义为:第i时的收银员人数满足小于等于num[i],且大于等于0
可变形为:
sum[i]>=sum[i-1]+0
即i-1 -->i 边权w=0

sum[i-1]>=sum[i]-num[i]
即i -->i-1 边权w=-num[i]

2)s[i]+s[i-1]+s[i-2]+s[i-3]+s[i-4]+s[i-5]+s[i-6]+s[i-7]>=r[i]

含义为:在第i时工作的收银员人数要大于实际需要的收银员人数
解释:因为每个人要工作8小时,所以第i时的工作人数要算上距今八个小时内开始工作的收银员人数。
可以变形为sum[i]-sum[i-8]>=r[i]
sum[i]>=sum[i-8]+r[i]
即i-8-->i 边权w=r[i]
对于此式 i的范围为[8,24]

3) sum[24]-sum[i+16]+sum[i]>=r[i]

含义为:从i时到前一天的i+16时开始工作的人数大于i时所需要的人数,即在第i时工作的收银员人数要大于实际需要的收银员人数。与2)不同点在于此式子适用于跨天工作
可变形为 sum[i]>=sum[i+16]+r[i]-sum[24]
即 i+16-->i 边权w=r[i]-sum[24]
对于此式 i的范围为[1,7]

4) sum[24]==c

等价于sum[24]>=c && sum[24]<=c
可以转化为sum[24]>=sum[0]+c && sum[0]>=sum[24]-c
即 0-->24 w=c && 24-->0 w=-c

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;


const int N=50,M=510,K=1010;
struct node{
	int from,to,next,d;
}f[M];int h[N],idx=0;
void add(int from,int to,int d)
{
	f[idx]={from,to,h[from],d};
	h[from]=idx;idx++;
}


int T,r[N],num[N],n;

int dist[N],cnt[N],v[N];

void build(int c)
{
	memset(dist,0xcf,sizeof dist);
	memset(v,0,sizeof v);
	memset(cnt,0,sizeof cnt);
	memset(h,-1,sizeof h);
	idx=0;
	add(0,24,c);add(24,0,-c);
	for(int i=1;i<=24;i++)
	{add(i-1,i,0);add(i,i-1,-num[i]);}
	for(int i=8;i<=24;i++) add(i-8,i,r[i]);
	for(int i=1;i<=7;i++) add(i+16,i,r[i]-c);
	return ;
}
bool spfa(int c)
{
	build(c);
	queue<int> q;
	q.push(0);v[0]=true;dist[0]=0;
	while(q.size())
	{
		int t=q.front();
		q.pop();
		
		v[t]=false;
		for(int i=h[t];~i;i=f[i].next )
		{
			int ver=f[i].to;
			if(dist[ver]<dist[t]+f[i].d)
			{
				dist[ver]=dist[t]+f[i].d;
				cnt[ver]=cnt[t]+1;
				if(cnt[ver]>=25) return false;
				if(!v[ver]) q.push(ver);
			}
		}
	}
	return true;
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		for(int i=1;i<=24;i++)
			scanf("%d",&r[i]);
		scanf("%d",&n);
		memset(num,0,sizeof num);
		for(int i=1;i<=n;i++)
		{
			int t;
			scanf("%d",&t);
			num[t+1]++;
		}
		bool success=false;
		for(int i=1;i<=1010;i++)
		{
			if(spfa(i))
			{
				cout<<i<<endl;
				success=true;
				break;
			}
		}
		if(success==false) printf("No Solution\n");
	}
	return 0;
}
posted @ 2022-04-30 00:15  天作玄机  阅读(43)  评论(0)    收藏  举报