从【poj1275/HDU1529】总结差分约束

题目大意

一家24小时商店想要招募一些员工,24小时的每一个时段需要的员工数不同。现在有n个人参加招聘,他们给出了一个要求t[i],表示如果招募第i个人,第i个人会从t[i]时刻开始工作8个小时。求商店最少雇佣多少员工?

关于差分约束

由经验(?)得知是差分约束。
现在我们把题目里的0时刻看作1时刻,1时刻看作2时刻以此类推,方便表述。
好的,我们有哪些约束条件呢?
现在我们令need[i]表示i时刻需要多少员工,have[i]表示从第i时刻开始工作的有多少员工,s[i]表示雇佣的1到i时刻开始工作的员工数,则:
s[x]-s[x-1]>=0
s[x]-s[x-1]<=have[i]
当1<=x<=7时,s[x]+(s[24]-s[24-(8-x)])>=need[x],即s[x]-s[16+x]>=need[x]-s[24]
当8<=x<=24时,s[x]-s[x-8]>=need[x]
这些式子没问题吧?(应该吧?)
然后我们就会发现一个很麻烦的式子:s[x]-s[16+x]>=need[x]-s[24],这让我怎么连边啊喂???
等等,既然如此我们不如二分查找s[24](即答案)的值吧,这是显然满足单调性的,如果我雇佣mid个员工是不够的,显然要多雇员点员工。如果够了,就可以考虑裁员啦!
这样子我们就可以轻易建图了(我是按照为了求最短路建的图),然而别忘了这个式子:
s[24]-s[0]>=ans(二分出来的答案)
这个是为什么呢?因为我们如果跑最短路,因为要求的东西是s[24]-s[0],所以要尽量小的是s[0],那么24节点是终点,0节点是起点。如果我们让s[24]初始为0的话,s[0]一定是一个负数。而s[0]又是跑最短路得到的,所以其他可行的s[0]一定比求出来的s[0]要大,也就是最后求得的ans=s[24]-s[0]会小于等于当前的s[24]-s[0]
(QAQ我好蠢啊就上面这一段我理解了2个小时,而且网上的大神都是一脸“这个理所当然”的样子)
那么什么情况下说明这个方案不可行呢?自然是出现负权回路,因为这个时候是求不出最短路的。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=1005;
int T,n,tot,bf;
int a[26],num[26],dis[26],js[26],inq[26];
int h[26],to[N<<1],ne[N<<1],w[N<<1];
int spfa(int lim){
    int i,x;
    queue<int>q;
    for(i=0;i<=24;++i)dis[i]=1e8,js[i]=inq[i]=0;
    dis[24]=0,inq[24]=1,q.push(24);
    while(!q.empty()){
        x=q.front(),q.pop(),inq[x]=0;
        for(i=h[x];i!=-1;i=ne[i])
            if(dis[x]+w[i]<dis[to[i]]){
            dis[to[i]]=dis[x]+w[i];
            if(!inq[to[i]]){
                inq[to[i]]=1,q.push(to[i]),++js[to[i]];
                if(js[to[i]]==25)return 0;
            }
        }
    }
    return 1;
}
void add(int x,int y,int z)
{to[++tot]=y,ne[tot]=h[x],h[x]=tot,w[tot]=z;}
void build(int lim){
    int i;tot=0;for(i=0;i<=24;++i)h[i]=-1;
    for(i=1;i<=24;++i)add(i,i-1,0),add(i-1,i,num[i]);
    for(i=8;i<=24;++i)add(i,i-8,-a[i]);
    for(i=1;i<=7;++i)add(i,16+i,lim-a[i]);
    add(24,0,-lim);
}
void work(){
    int l=0,r=n,mid,ans=-1;
    while(l<=r){
        mid=(l+r)>>1,build(mid);
        if(spfa(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    if(ans==-1)printf("No Solution\n");
    else printf("%d\n",ans);
}
int main(){
    int i,x;
    scanf("%d",&T);
    while(T--){
        for(i=1;i<=24;++i)scanf("%d",&a[i]),num[i]=0;
        scanf("%d",&n);
        for(i=1;i<=n;++i)scanf("%d",&x),++num[x+1];
        work();
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值