Cashier Employment POJ - 1275

题面

题意

一家超市24小时营业,给出24小时每小时至少需要的员工数量和n名员工的申请开工时间,每名员工连续工作8小时,问最少雇佣员工数量.

做法

利用前缀和可以发现题目可以转化为多个形如x+y<=b的不等式,因而用差分约束系统求解,s[i]表示0到i时雇佣的员工数量和,可以得到以下方程:
1.s[i]>=s[i-1],每小时雇佣的员工数量大于等于0.
2.s[i]-s[i+16]>=need[i]-n (i<8,n表示所选员工总数) , s[i]-s[i-8]>=need[i](i >=8),满足每小时需要的员工数
3.s[24]-s[0]<=n
4.s[i]-s[i-1]<=have[i]
二分答案,用这种方法判断是否可行

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f
#define N 30
#define M 100100
using namespace std;

int n,m,T,bb,need[N],have[N],first[N],l,r,mid,d[N],cur[N],bj,cnt[N];
bool in[N];
struct Bn
{
    int to,next,quan;
} bn[M];
queue<int>que;

inline void add(int u,int v,int w)
{
    bb++;
    bn[bb].to=v;
    bn[bb].quan=w;
    bn[bb].next=first[u];
    first[u]=bb;
}

inline bool check(int u)
{
    int i,j,p,q;
    bb=bj;
    for(i=0;i<=24;i++)
    {
        first[i]=cur[i];
    }
    for(i=1; i<=7; i++)
    {
        add(i,i+16,u-need[i]);
    }
    add(24,0,-u);
    for(; !que.empty(); que.pop());
    memset(in,0,sizeof(in));
    memset(d,INF,sizeof(d));
    memset(cnt,0,sizeof(cnt));
    que.push(24);
    d[24]=u;
    for(; !que.empty();)
    {
        q=que.front();
        que.pop();
        in[q]=0;
        cnt[q]++;
        for(p=first[q]; p!=-1; p=bn[p].next)
        {
            if(d[bn[p].to]>d[q]+bn[p].quan)
            {
                d[bn[p].to]=d[q]+bn[p].quan;
                if(!in[bn[p].to])
                {
                    in[bn[p].to]=1;
                    que.push(bn[p].to);
                }
            }
        }
        if(cnt[q]>25) return 0;
    }
    return (!d[0]);
}

int main()
{
    int i,j,p,q;
    cin>>T;
    while(T--)
    {
        bb=0;
        memset(have,0,sizeof(have));
        memset(first,-1,sizeof(first));
        for(i=1; i<=24; i++)
        {
            scanf("%d",&need[i]);
        }
        scanf("%d",&n);
        for(i=1; i<=n; i++)
        {
            scanf("%d",&p);
            have[++p]++;
        }
        for(i=1; i<=24; i++)
        {
            add(i,i-1,0);
        }
        for(i=8; i<=24; i++)
        {
            add(i,i-8,-need[i]);
        }
        for(i=1; i<=24; i++)
        {
            add(i-1,i,have[i]);
        }
        bj=bb;

        for(i=0;i<=24;i++)
        {
            cur[i]=first[i];
        }

        l=1;
        r=n;
        if(!check(n))
        {
            puts("No Solution");
            continue;
        }
        for(; l+1<r;)
        {
            mid=(l+r)>>1;
            check(mid)?r=mid:l=mid;
        }
        if(l==r)
        {
            printf("%d\n",l);
            continue;
        }
        check(l)?printf("%d\n",l):printf("%d\n",r);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值