题意:有一家商店需要24小时营业,每个小时内店内都需要一定工作人员。现在有n个人报名工作,每个人有一个开始上班时间并且每个人一天只工作8个小时(从上班时间开始计算),问至少雇佣多少人才可以使每一个小时内工作人员数目都满足要求,若无解则输出No Solution。
思路:若定义dis[i]表示第i个小时之前至少需要雇佣人数,那么我们就可以得到一系列的不等式,例如:dis[8]-dis[0]>=r[8]。有了不等式之后,我们就可以建图spfa最长路求最小值。但是现在有个问题对于(0<i<7)这类下标,不等式就会变为dis[i]-dis[i+16]+dis[24]>=r[i](dis[24]就是答案 )。那问题如何解决呢,第一种方法暴力枚举dis[24]答案小于等于n可以通过但效率不高,第二种方法二分答案(因为答案具有线性,越大的答案一定越可行)。建图还需建立相邻两点间的限制条件0<=dis[i]-dis[i-1]<=s[i](来面试者中,上班时间为i的人数)以及dis[24]-dis[0]==24。
C++代码:
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int tol,head[30];
struct edge
{
int to,cost,next;
}es[3010];
void init()
{
tol = 0;
memset ( head , -1 , sizeof(head) );
}
void addedge( int u , int v , int w )
{
es[tol].to = v;
es[tol].cost = w;
es[tol].next = head[u];
head[u] = tol++;
}
int r[30];
int s[30];
int dis[30];
int vis[30];
bool spfa( int mid )
{
memset ( dis , -inf , sizeof(dis) );
memset ( vis , 0 , sizeof(vis) );
queue<int>Q;
Q.push(0);
vis[0] = 1;
dis[0] = 0;
while ( !Q.empty() )
{
int u = Q.front();
Q.pop();
vis[u] = 0;
if ( dis[24]>mid )
return false;
for ( int i=head[u] ; i!=-1 ; i=es[i].next )
{
int v = es[i].to,w = es[i].cost;
if ( dis[v]<dis[u]+w )
{
dis[v] = dis[u]+w;
if ( !vis[v] )
{
vis[v] = 1;
Q.push(v);
}
}
}
}
return dis[24]==mid;
}
int main()
{
int T; scanf ( "%d" , &T );
while ( T-- )
{
memset( s , 0 , sizeof(s) );
for ( int i=0 ; i<24 ; i++ )
scanf ( "%d" , &r[i] );
int n; scanf ( "%d" , &n );
for ( int i=1 ; i<=n ; i++ )
{
int x; scanf ( "%d" , &x );
s[x]++;
}
int L=0,R=n,mid,ans=n+1;
while( L<=R )
{
mid = (L+R)>>1;
init();
for ( int i=0 ; i<24 ; i++ )
{
if ( i<7 )
addedge( i+17 , i+1 , r[i]-mid );
else
addedge( i-07 , i+1 , r[i] );
addedge( i , i+1 , 0 );
addedge( i+1 , i , -s[i] );
}
addedge( 0 , 24 , mid );
addedge( 24 , 0 , -mid );
if ( spfa( mid ) )
R = mid-1,ans = min( ans , mid );
else
L = mid+1;
}
if ( ans==n+1 )
printf ( "No Solution\n" );
else
printf ( "%d\n" , ans );
}
return 0;
}