分 析 过 程 \color{Red}分析过程 分析过程

为了方便,能在 0 0 0时刻雇佣的人看作第一个人

令 p r e [ i ] pre[i] pre[i]为 [ 1 , i ] [1,i] [1,i]中雇佣的人

n u m [ i ] num[i] num[i]为 i i i时刻能雇佣的人, l i m i t [ i ] limit[i] limit[i]为 i i i时刻应该雇佣的人

p r e [ i ] − p r e [ i − 1 ] > = 0 pre[i]-pre[i-1]>=0 pre[i]−pre[i−1]>=0

p r e [ i ] − p r e [ i − 1 ] < = n u m [ i ] pre[i]-pre[i-1]<=num[i] pre[i]−pre[i−1]<=num[i]

对于 i > = 8 i>=8 i>=8时, p r e [ i ] − p r e [ i − 8 ] > = l i m i t [ i ] pre[i]-pre[i-8]>=limit[i] pre[i]−pre[i−8]>=limit[i]

对于 7 > = i > = 1 7>=i>=1 7>=i>=1, p r e [ 24 ] − p r e [ 24 − ( 8 − i ) ] + p r e [ i ] > = l i m i t [ i ] pre[24]-pre[24-(8-i)]+pre[i]>=limit[i] pre[24]−pre[24−(8−i)]+pre[i]>=limit[i]

化简得到 p r e [ i ] − p r e [ 16 + i ] > = l i m i t [ i ] − p r e [ 24 ] pre[i]-pre[16+i]>=limit[i]-pre[24] pre[i]−pre[16+i]>=limit[i]−pre[24]

出现了三个变量!!差分约束也没办法解决这个

但是我们可以直接二分答案也就是 p r e [ 24 ] pre[24] pre[24]的值

那么得到 p r e [ i ] > = p r e [ 16 + i ] + l i m i t [ i ] − m i d pre[i]>=pre[16+i]+limit[i]-mid pre[i]>=pre[16+i]+limit[i]−mid

如 何 连 边 \color{Red}如何连边 如何连边

对于 p r e [ i ] > = p r e [ i − 1 ] pre[i]>=pre[i-1] pre[i]>=pre[i−1]

由 i − 1 i-1 i−1向 i i i连一条边权 0 0 0的边

对于 p r e [ i − 1 ] > = p r e [ i ] − n u m [ i ] pre[i-1]>=pre[i]-num[i] pre[i−1]>=pre[i]−num[i]

由 i i i向 i − 1 i-1 i−1连一条边权 − n u m [ i ] -num[i] −num[i]的边

对于 p r e [ i ] > = p r e [ i − 8 ] + l i m i t [ i ] pre[i]>=pre[i-8]+limit[i] pre[i]>=pre[i−8]+limit[i]

由 i − 8 i-8 i−8向 i i i连一条费用 l i m i t [ i ] limit[i] limit[i]的边

对于 p r e [ i ] > = p r e [ 16 + i ] + l i m i t [ i ] − m i d pre[i]>=pre[16+i]+limit[i]-mid pre[i]>=pre[16+i]+limit[i]−mid

由 16 + i 16+i 16+i向 i i i连一条费用 l i m i t [ i ] − m i d limit[i]-mid limit[i]−mid的边

最后 p r e [ 24 ] − p r e [ 0 ] > = m i d pre[24]-pre[0]>=mid pre[24]−pre[0]>=mid且 p r e [ 24 ] − p r e [ 0 ] < = m i d pre[24]-pre[0]<=mid pre[24]−pre[0]<=mid

由 0 0 0连向 24 24 24费用 m i d mid mid,由 24 24 24连向 0 0 0费用 − m i d -mid −mid

只要不存在负环就是可以的

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
const int inf = 1e9;
int t,n,m,num[30000],limit[30];
struct edge{
int to,nxt,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int w)
{
d[++cnt] = (edge){v,head[u],w}, head[u] = cnt;
}
int dis[maxn],vis[maxn],shu[maxn];
bool spfa()
{
queue<int>q;
for(int i=0;i<=24;i++) vis[i] = shu[i] = 0, dis[i] = -inf;
q.push( 0 ); dis[0] = 0;
while( !q.empty() )
{
int u = q.front(); q.pop();
vis[u] = 0;
for(int i=head[u];i;i=d[i].nxt )
{
int v = d[i].to;
if( dis[v] < dis[u]+d[i].w )
{
dis[v] = dis[u]+d[i].w;
if( !vis[v] )
{
vis[v] = 1; q.push( v );
if( ++shu[v]==25 ) return false;
}
}
}
}
return 1;
}
bool isok(int mid)
{
for(int i=1;i<=24;i++)
{
add( i-1,i,0 ), add( i,i-1,-num[i] );
if( i>=8 ) add(i-8,i,limit[i] );
else add( 16+i,i,limit[i]-mid );
}
add(0,24,mid); add(24,0,-mid);
bool flag = spfa();
cnt = 1;
for(int i=0;i<=24;i++) head[i] = 0;
return flag;
}
int main()
{
cin >> t;
while( t-- )
{
for(int i=1;i<=24;i++) cin >> limit[i];
cin >> m;
for(int i=1;i<=m;i++)
{
int x; scanf("%d",&x);
num[x+1]++;
}
int l=0,r=m,ans=-1;
while( r>=l )
{
int mid = l+r>>1;
if( isok(mid) ) r = mid-1, ans = mid;
else l = mid+1;
}
if( ans==-1 ) printf("No Solution\n");
else cout << ans << endl;
for(int i=0;i<=24;i++) num[i] = 0;
}
}