Hdu 1529 差分约束 解题报告

本文介绍了一种利用差分约束系统解决超市收银员排班问题的方法。通过建立图模型,采用二分查找和SPFA算法求解最小雇员数量,确保在不同时间段满足所需的最低收银员数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Cashier Employment

Problem 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

题意:

  超市在每个时间都有需要的人数(24小时)比如 1 0 0 0 0 。。。。也就是说在第0个小时的时候要用一个人,其他的时间都不用人,在给你一些人工作的起始时间,如果雇佣了这个人,那么这个人就会从自己的其实时间工作8个小时后离开,给你需求和可雇佣的员工,问你满足需求超时最少雇佣多少人。

思路:

经典的差分约束,之前尝试过很多次都没AC,今天终于AC了,现在我们就来找各种隐含条件。

假设:

num[i] 表示第i个小时开始的有多少个人。
r[i] 表示第i个小时最少雇佣多少人。
s[i] 表示1。。。i小时开始工作的有多少人。 (我们以S为核心建图)

限制条件:

第i个小时雇佣并开始工作的人数 >= 0
则 s[i] - s[i-1] >= 0
第i个小时雇佣并开始工作的人数 <= num[i]
则 s[i] - s[i-1] <= num[i] 转化成 s[i-1] - s[i] >= -num[i]
第i个小时雇佣的人数 >= r[i]

s[i] - s[i-8] >= r[i] (i >= 8 && i <= 24) s[24] +
s[i] - s[i + 16] >= r[i] (i <= 7)

观察最后一个不等式,出现了三个变量,不符合差分约束形式,所以我们就直接二分枚举
s[24]的值,也就是二分枚举雇佣人数的值,这样就把最后一个转换成
s[i] - s[i + 16] >= r[i] - mid
最后别忘了还有一个限制条件就是s[24] - s[0] = mid,=怎么建边呢?我们可以这样

s[24] - s[0] >= mid并且 s[24] - s[0] <= mid

第二个转换成 s[0] - s[24] >= -mid;
这样就可以二分下去了。。。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
const int M=30+5;
const int N=10000+5;
const int INF=1000000000;
using namespace std;
typedef struct
{
   int to ,cost ,next;
}STAR;
STAR E[N];
int list[M],tot,s_x[M],r[30],num[1100];
void add(int a ,int b ,int c)
{
   E[++tot].to=b;
   E[tot].cost=c;
   E[tot].next=list[a];
   list[a]=tot;
}
int Spfa(int s ,int n)
{
   for (int i=0;i<=n;i++)
   s_x[i]=-INF;
   int mark[M]={0};
   int in[M]={0};
   s_x[s]=0;
   mark[s]=in[s]=1;
   queue<int> q;
   q.push(s);
   while(!q.empty())
   {
      int xin,tou;
      tou=q.front();
      q.pop();
      mark[tou]=0;
      for (int k=list[tou];k;k=E[k].next)
      {
         xin=E[k].to;
         if (s_x[xin]<s_x[tou]+E[k].cost)
         {
            s_x[xin]=s_x[tou]+E[k].cost;
            if (!mark[xin])
            {
               mark[xin]=1;
               if (++in[xin]>n) return 0;
               q.push(xin);
            }
        }
      }
   }
   return 1;
}

bool ok(int mid)
{
   memset(list,0,sizeof(list));
   tot=1;
   for (int i=1;i<=24;i++)
   {
      add(i-1,i,0);
      add(i,i-1,-num[i]);
      if (i>=8) add(i-8,i,r[i]);
      else add(i+16,i,r[i]-mid);
   }
   add(0,24,mid);
   add(24,0,-mid);
   return Spfa(0,24);
}

int main ()
{
   int t,i,a,n;
   scanf("%d",&t);
   while(t--)
   {
      for (i=1;i<=24;i++)
      scanf("%d",&r[i]);
      scanf("%d",&n);
      memset(num,0,sizeof(num));
      for (i=1;i<=n;i++)
      {
         scanf("%d",&a);
         num[a+1]++;
      }
      int low,mid,up;
      low=0,up=n;
      int ans=-1;
      while(low<=up)
      {
         mid=(low+up)>>1;
         if(ok(mid))
         {
            ans=mid;
            up=mid-1;
         }
         else low=mid+1;
      }
      if(ans==-1) puts("No Solution");
      else printf("%d\n",ans);
   }
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值