题目描述
德黑兰的一家每天24小时营业的超市,需要一批出纳员来满足它的需求。超市经理雇佣你来帮他解决一个问题————超市在每天的不同时段需要不同数目的出纳员(例如,午夜只需一小批,而下午则需要很多)来为顾客提供优质服务,他希望雇佣最少数目的纳员。
超市经历已经提供一天里每一小时需要出纳员的最少数量————R(0),R(1),...,R(23)。R(0)表示从午夜到凌晨1:00所需要出纳员的最少数目;R(1)表示凌晨1:00到2:00之间需要的;等等。每一天,这些数据都是相同的。有N人申请这项工作,每个申请者i在每天24小时当中,从一个特定的时刻开始连续工作恰好8小时。定义ti(0<=ti<=23)为上面提到的开始时刻,也就是说,如果第i个申请者被录用,他(或她)将从ti时刻开始连续工作8小时。
试着编写一个程序,输入R(i),i=0,...,23,以及ti,i=1,...,N,它们都是非负整数,计算为满足上述限制需要雇佣的最少出纳员数目、在每一时刻可以有比对应R(i)更多的出纳员在工作
输入
输入文件的第1行为一个整数T,表示输入文件中测试数据的数目(至多20个)。每个测试数据第一行为24个整数,表示R(0),R(1),...,R(23),R(i)最大可以取到1000。接下来一行是一个整数N,表示申请者的数目,0<=N<=1000。接下来有N行,每行为一个整数ti,0<=ti<=23,测试数据之间没有空行。
输出
对输入文件中的每个测试数据,输出占一行,为需要雇佣的出纳员的最少数目。如果某个测试数据没有解。则输出"No Solution"。
样例输入
1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 5 0 23 22 1 10
样例输出
1
提示
关键点依然在于怎么建边,首先数组dist[i]表示前i(包括i)个小时雇佣的总人数,然后题目中的时间是0-23点,为了方便程序书写把所有时间点加1变成1-24点,题目中的need[i]数组表示第i点需要的最少人数,work[i]表示i点进入工作的人数。
对于每一个i点,新加入的人数可以为work[i]也可以不加,所以我们建两条边i-1->i点权值为0的边,i->i-1权值为-work[i]的边。
如果下标i>8,那么i时工作的人数=dist[i]-dist[i-8],就得到了式子dist[i]-dist[i-8]>=need[i],所以我们要建i-8 -> i权值为need[i]的边;如果下标i<8,那么i时工作的人数=dist[i]+dist[24]-dist[i+24-8],得到式子dist[i]+dist[24]-dist[i+16]>=need[i],而dist[24]也就是要求的最少人数sum,所以dist[i]-dist[i+16]>=need[i]+sum,建一条i+16->i权值为need[i]-sum的边。最后再建一条从0到24的权值为sum的边。
然后从0开始枚举需要的人数进入spfa即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define sc(x){scanf("%d",&x);}
#define INF 0x7f7f7f7f
int n,tot=0,need[25],work[25],head[25],dist[25],vis[25],used[25];
struct Edge
{
int to,next,weight;
}edge[200000];
void add_edge(int from,int to,int weight)
{
edge[++tot].to=to;
edge[tot].weight=weight;
edge[tot].next=head[from];
head[from]=tot;
}
int spfa(int sum)
{
for(int i=0;i<=24;i++)
{
dist[i]=-INF;
vis[i]=0;
used[i]=0;
}
dist[0]=0;
vis[0]=1;
used[0]=1;
queue<int>q;
q.push(0);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to,w=edge[i].weight;
if(dist[v]<dist[u]+w)
{
dist[v]=dist[u]+w;
if(!vis[v])
{
vis[v]=1;
used[v]++;
if(used[v]>24)
return 0;
q.push(v);
}
}
}
}
return dist[24]==sum;
}
int main()
{
int t;
sc(t);
while(t--)
{
bool flag=false;
memset(need,0,sizeof(need));
memset(work,0,sizeof(work));
for(int i=1;i<25;i++)
sc(need[i]);
sc(n);
for(int i=1;i<=n;i++)
{
int x;
sc(x);
work[x+1]++;//由于题目是从0-23,我们在程序中1-24好写,所以我们需要把每个数加1
}
for(int k=0;k<=n;k++)//枚举需要的人数
{
tot=0;
memset(head,-1,sizeof(head));
for(int i=1;i<25;i++)
{
add_edge(i-1,i,0);
add_edge(i,i-1,-work[i]);
if(i>=8)
add_edge(i-8,i,need[i]);
else
add_edge(16+i,i,need[i]-k);
}
add_edge(0,24,k);//建一条从0->24的边,权值为k,表示前24小时内总雇佣的人数
if(spfa(k))
{
flag=true;
cout<<k<<endl;
break;
}
}
if(flag==false)
cout<<"No Solution"<<endl;
}
return 0;
}