正题
题目链接:http://poj.org/problem?id=1275
题目大意
1
∼
24
1\sim 24
1∼24小时中第
i
i
i个小时需要
r
i
r_i
ri个出纳员
有
n
n
n个人应聘,第
i
i
i从
x
i
x_i
xi开始工作,一直工作8个小时。
求至少要招募多少人应聘。
解题思路
n
u
m
i
num_i
numi表示第
i
i
i个小时有多少人招聘。
设定
k
i
k_i
ki表示第
i
i
i个小时放多少人
这时需要
k
i
≥
r
I
k_i\geq r_I
ki≥rI且
∑
i
=
0
7
n
u
m
(
n
−
i
+
24
)
%
24
+
1
≤
k
i
\sum_{i=0}^7num_{(n-i+24)\%24+1}\leq k_i
∑i=07num(n−i+24)%24+1≤ki
这时我们要设定差分约束
s
i
=
∑
i
=
1
i
s
i
s_i=\sum_{i=1}^is_i
si=∑i=1isi
然后得出
s
i
−
s
i
−
1
≥
0
s_i-s_{i-1}\geq 0
si−si−1≥0
s
i
−
1
−
s
i
≥
−
n
u
m
i
s_{i-1}-s_i\geq -num_i
si−1−si≥−numi
s
i
−
s
i
−
8
≥
r
i
s_i-s_{i-8}\geq r_i
si−si−8≥ri
s
i
−
s
i
+
16
≥
r
i
−
s
24
s_i-s_{i+16}\geq r_i-s_{24}
si−si+16≥ri−s24
然后因为最后一个式子有三个未知量,所以我们枚举
s
24
s_{24}
s24就好了
c o d e code code
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=30;
struct edge{
int to,next,w;
}a[N*8];
queue<int> q;
int T,n,tot;
int r[N],f[N],cnt[N],num[N],ls[N];
bool v[N],flag;
void addl(int x,int y,int w)
{
a[++tot].to=y;
a[tot].w=w;
a[tot].next=ls[x];
ls[x]=tot;
}
void build(int x)
{
tot=0;
memset(ls,0,sizeof(ls));
addl(0,24,x);
for(int i=1;i<=24;i++)
{
addl(i-1,i,0);
addl(i,i-1,-num[i]);
if(i>=8) addl(i-8,i,r[i]);
else addl(i+16,i,r[i]-x);
}
}
int spfa(int ans)
{
memset(f,0xcf,sizeof(f));
memset(cnt,0,sizeof(cnt));
memset(v,0,sizeof(v));
while(!q.empty()) q.pop();
q.push(0);f[0]=0;v[0]=1;
while(!q.empty())
{
int x=q.front();v[x]=0;q.pop();
for(int i=ls[x];i;i=a[i].next)
{
int y=a[i].to;
if(f[x]+a[i].w>f[y])
{
f[y]=f[x]+a[i].w;
if(!v[y]){
q.push(y);
v[y]=1;
}
if(++cnt[y]>24)
return 0;
}
}
}
if(f[24]==ans) return 1;
return 0;
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(num,0,sizeof(num));
for(int i=1;i<=24;i++)
scanf("%d",&r[i]);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
num[x+1]++;
}
flag=0;
for(int i=0;i<=n;i++)
{
build(i);
if(spfa(i)){
flag=true;
printf("%d",i);
break;
}
}
if(!flag) printf("No Solution");
putchar('\n');
}
}