poj 1275 Cashier Employment

Cashier Employment
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 7048 Accepted: 2618

Description

A supermarket in Tehran is open 24 hours a day every day and needs a number of cashiers to fit its need. The supermarket manager has hired you to help him, solve his problem. The problem is that the supermarket needs different number of cashiers at different times of each day (for example, a few cashiers after midnight, and many in the afternoon) to provide good service to its customers, and he wants to hire the least number of cashiers for this job. 

The manager has provided you with the least number of cashiers needed for every one-hour slot of the day. This data is given as R(0), R(1), ..., R(23): R(0) represents the least number of cashiers needed from midnight to 1:00 A.M., R(1) shows this number for duration of 1:00 A.M. to 2:00 A.M., and so on. Note that these numbers are the same every day. There are N qualified applicants for this job. Each applicant i works non-stop once each 24 hours in a shift of exactly 8 hours starting from a specified hour, say ti (0 <= ti <= 23), exactly from the start of the hour mentioned. That is, if the ith applicant is hired, he/she will work starting from ti o'clock sharp for 8 hours. Cashiers do not replace one another and work exactly as scheduled, and there are enough cash registers and counters for those who are hired. 

You are to write a program to read the R(i) 's for i=0..23 and ti 's for i=1..N that are all, non-negative integer numbers and compute the least number of cashiers needed to be employed to meet the mentioned constraints. Note that there can be more cashiers than the least number needed for a specific slot. 

Input

The first line of input is the number of test cases for this problem (at most 20). Each test case starts with 24 integer numbers representing the R(0), R(1), ..., R(23) in one line (R(i) can be at most 1000). Then there is N, number of applicants in another line (0 <= N <= 1000), after which come N lines each containing one ti (0 <= ti <= 23). There are no blank lines between test cases.

Output

For each test case, the output should be written in one line, which is the least number of cashiers needed. 
If there is no solution for the test case, you should write No Solution for that case. 

Sample Input

1
1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
5
0
23
22
1
10

Sample Output

1

一道差分约束的好题,设num[i]表示可以在i时刻工作的人数,x[i]表示实际在i时刻工作的人数,r[i]表示i时刻最少需要工作的人数。s[i]=x[1]+x[2]+...+x[i],这里为了防止数据越界,同样下标同步+1;

x[i]<=num[i],即s[i]-s[i-1]<=num[i],这里注意了,x[i]>=0(WA了好多次),也就是s[i-1]-s[i]<=0

然后有s[i]-s[i-8]>=r[i],8<=r<=24,也即s[i-8]-s[i]<=-r[i]

s[i]-s[0]+s[24]-s[i+16]>=r[i],1<=i<=7,也即s[i]-s[i+16]>=r[i]+s[0]-s[24],所以s[i+16]-s[i]<=s[24]-s[0]-r[i]

注意s[0]不一定求出来是0,虽然我们令它为0.设sum=s[24]-s[0]

这里二分枚举sum,因为我们要求的也是s[24]-s[0],注意这里的s[24]-s[0]还应看成变量,而不能与sum(被当做常量)划等号。我们在这里加个限制条件:s[24]-s[0]>=sum,也就是s[0]-s[24]<=-sum,这样保证在有解的情况下,s[24]-s[0]>=sum,这里有些不容易理解的地方出来了,我暂且说说我的理解。

在有解情况下,那么可能有s[24]-s[0]>sum而s[24]-s[0]!=sum,这样与我们之前令sum=s[24]-s[0]是矛盾的,这样的有解只能算无解。但我觉得不会出现这种情况,第一,因为我们是根据约束条件来求解的,其中约束条件是s[24]-s[0]>=sum而不是s[24]-s[0]>sum,前者多了一个等号,自然解集包含的范围比后者要广泛,所以前者的解集减去后者就是s[24]-s[0]=sum的情况。但如果两个的差集为空集呢?首先这是个实际问题,如果存在解的,那么必然存在s[24]-s[0]=sum的情况,所以必然会被解出来,因此如果有解,我觉得两个的差集不会为空集,如果无解那么s[24]-s[0]!=sum,那么我认为s[24]-s[0]不会大于sum,要相信解的分布是连续的,并且按照实际问题的性质,sum越大,有解的几率越高,因此可以感觉到s[24]-s[0]一开始小于sum,之后在某点出现s[24]-s[0]=sum,之后就是s[24]-s[0]>=sum(这里显然包括s[24]-s[0]=sum)。

以上纯属个人见解,如有不同看法希望交流!

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#define Maxn 1010
using namespace std;

struct line{
    int u,v,w;
    line(int uu=0,int vv=0,int ww=0):u(uu),v(vv),w(ww){}
}p[Maxn];
int dist[30],r[30],num[30];
int tot;
const int inf=0x3f3f3f3f;
void addedge(int u,int v,int w){
    p[tot++]=line(u,v,w);
}
bool spfa(int u){
    for(int i=0;i<=24;i++)
        dist[i]=inf;
    dist[u]=0;
    for(int i=0;i<24;i++)
        for(int j=0;j<tot;j++){
            int u=p[j].u,v=p[j].v,w=p[j].w;
            if(dist[u]!=inf&&dist[u]+w<dist[v])
                dist[v]=dist[u]+w;
        }
    for(int j=0;j<tot;j++){
            int u=p[j].u,v=p[j].v,w=p[j].w;
            if(dist[u]!=inf&&dist[u]+w<dist[v])
                return false;
        }
    return true;
}
int main()
{
    int t,n,x;
    scanf("%d",&t);
    while(t--){
        for(int i=1;i<=24;i++)
            scanf("%d",r+i);
        scanf("%d",&n);
        memset(num,0,sizeof num);
        for(int i=0;i<n;i++){
            scanf("%d",&x);
            num[x+1]++;
        }
        tot=0;
        for(int i=1;i<=24;i++){
            addedge(i,i-1,0);
            addedge(i-1,i,num[i]);
        }
        for(int i=8;i<=24;i++)
            addedge(i,i-8,-r[i]);
        int l=0,rr=n+1,tmp=tot;
        while(l<rr){
            int mid=l+rr>>1;
            tot=tmp;
            for(int i=1;i<=7;i++)
                addedge(i,i+16,mid-r[i]);
            addedge(24,0,-mid);
            if(spfa(0)) rr=mid;
            else l=mid+1;
        }
        if(l==n+1) puts("No Solution");
        else printf("%d\n",l);
    }
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值