出纳员问题

https://loj.ac/problem/10088

题目描述

  有一家24小时超市招收出纳员,每个时刻需要r[i]个人,有n个竞聘者,他们会从t[i]开始,连续工作8小时,求招收的最少出纳员。

思路

  首先我们考虑每个竞聘者的具体时间没有意义,我们可以直接记着n个人中在i时刻开始工作的人数为num[i],为了方便处理,我们把时间改为1~24。那我们再考虑限制条件,假设x[i]表示i时刻实际工作人数,那么我们记x[i]的前缀和为s[i],由此可以得到几个等式:

  ①s[i]-s[i-1]≤num[i],s[i-1]≤s[i]

  ②当9≤i≤24时,s[i]-s[i-8]≥r[i]

  ③当1≤i≤8时,s[24]+s[i]-s[i+16]≥r[i]

由此我们就可以通过移项得到差分约束所需要的式子,不过第三个式子有一些特殊,它包含一个s[24],不过分析可知s[24]就是实际雇佣的总人数,所以我们可以二分总人数,对于每个总人数建出图后,用dfs判断图上是否存在负环即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1100,M=1e4+10;
int nxt[M],to[M],w[M],head[N],tot;
int dis[N],num[30],r[30],vis[N];
bool f;
void add_edge(int x,int y,int v)
{
    nxt[++tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    w[tot]=v;
}
void clear()
{
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(dis,0,sizeof(dis));
    tot=0;f=0;
}
void spfa(int u,int st)
{
    if(f)return ;
    vis[u]=st;
//    cout<<u<<endl;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(dis[v]<dis[u]+w[i])
        {
            dis[v]=dis[u]+w[i];
            if(!vis[v])spfa(v,st);
            if(vis[v]==st)
            {
                f=1;return ;
            }
        }
    }    
    vis[u]=0;
}
bool check(int k)
{
    clear();
    for(int i=1;i<=24;i++)
    {
        add_edge(i-1,i,0);
        add_edge(i,i-1,-num[i]);
    }
    for(int i=9;i<=24;i++)
        add_edge(i-8,i,r[i]);
    for(int i=1;i<=8;i++)
        add_edge(i+16,i,r[i]-k);
    add_edge(0,24,k);
    for(int i=1;i<=24;i++)
    {
        spfa(i,i);
        if(f)return 0;
    }
    return 1;
}
int main() 
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        memset(num,0,sizeof(num));
        int n;
        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]++;
        }
        int l=0,r=n,ans=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid))ans=mid,r=mid-1;
            else l=mid+1;
        }
//        cout<<check(1)<<endl;
        if(ans!=-1)printf("%d\n",ans);
        else printf("No Solution\n");
    }
    return 0;
}

 

posted @ 2019-10-22 07:46  fbz  阅读(180)  评论(0编辑  收藏  举报