断断续续做了3天才弄明白,真是一道经典题
/*
HDOJ 1529 Cashier Employment
s[i]保存从24时刻开始到i时刻的雇佣人数
t[i]保存i时刻能够上班的人数
r[i]保存i时刻至少需要的人数
只要开始工作就要连续工作8小时,有下面的约束条件
0 <= s[i]-s[i-1] <= t[i]
s[i]-s[i-8] >= r[i] (8 <= i <= 23)
s[23] - (s[i+16] - s[i]) >= r[i] (0 <= i <= 7)
s[23] - s[24] >= result
整理为下面的工整的约束条件
s[i-1] - s[i] <= 0 这个条件在第三和第四个条件的时候被更新了,所以不用加这个也没事
s[i] - s[i-1] <= t[i]
s[i-8] - s[i] <= - r[i] (8 <= i <= 23)
s[i+16] - s[i] <= -r[i]+result (0 <= i <= 7)
s[24] -s[23] <= -result
result 是未知的,但是范围就在1到申请人总数之间,用二分枚举
要求的是满足要求的最小值
*/
#include <iostream>
using namespace std;
#define maxn 25
#define mx 1000
int r[maxn];
int t[maxn];
int d[maxn];
struct E
{
int u,v,w;
};
E edge[mx];
int edno;
bool Bellman_Ford(int sum)
{
int i,j,t;
bool change;
int m=edno;
//s[i+16] - s[i] <= -r[i]+result
for(i=0;i<=7;i++)
{
edge[m].u=i;
edge[m].v=i+16;
edge[m++].w=-(r[i]-sum);
}
//s[24] -s[23] <= -result
edge[m].u=23;
edge[m].v=24;
edge[m++].w=-sum;
for(i=0;i<=24;i++)
d[i]=0;
for(i=0;i<=24;i++)
{
change=false;
for(j=0;j<m;j++)
{
t=d[edge[j].u]+edge[j].w;
if(d[edge[j].v]>t)
{
d[edge[j].v]=t;
change=true;
}
}
}
if(change)
return false;
return true;
}
int main()
{
int ncase,i,n,tmp,left,right,mid,res;
cin>>ncase;
while(ncase--)
{
for(i=0;i<24;i++)
cin>>r[i];
edno=0;
memset(t,0,sizeof(t));
cin>>n;
for(i=0;i<n;i++)
{
cin>>tmp;
t[tmp]++;
}
//s[i] - s[i-1] <= t[i]
edge[edno].u=24;
edge[edno].v=0;
edge[edno++].w=t[0];
for(i=1;i<=23;i++)
{
edge[edno].u=i-1;
edge[edno].v=i;
edge[edno++].w=t[i];
}
//s[i-8] - s[i] <= - r[i]
for(i=8;i<=23;i++)
{
edge[edno].u=i;
edge[edno].v=i-8;
edge[edno++].w=-r[i];
}
left=0;right=n;
res=-1;
while(left<right)
{
mid=(left+right)/2;
if(Bellman_Ford(mid))
{
res=mid;
right=mid;
}
else
{
left=mid+1;
}
}
if(res==-1)
cout<<"No Solution"<<endl;
else
cout<<res<<endl;
}
return 0;
}