【ACWing】393. 雇佣收银员

题目地址:

https://www.acwing.com/problem/content/395/

一家超市要每天 24 24 24小时营业,为了满足营业需求,需要雇佣一大批收银员。已知不同时间段需要的收银员数量不同,为了能够雇佣尽可能少的人员,从而减少成本,这家超市的经理请你来帮忙出谋划策。经理为你提供了一个各个时间段收银员最小需求数量的清单 R ( 0 ) , R ( 1 ) , R ( 2 ) , … , R ( 23 ) R(0),R(1),R(2),…,R(23) R(0),R(1),R(2),,R(23) R ( 0 ) R(0) R(0)表示午夜00:00到凌晨01:00的最小需求数量, R ( 1 ) R(1) R(1)表示凌晨01:00到凌晨02:00的最小需求数量,以此类推。一共有 N N N个合格的申请人申请岗位,第 i i i个申请人可以从 t i t_i ti时刻开始连续工作 8 8 8小时。收银员之间不存在替换,一定会完整地工作 8 8 8小时,收银台的数量一定足够。现在给定你收银员的需求清单,请你计算最少需要雇佣多少名收银员。

输入格式:
第一行包含一个不超过 20 20 20的整数,表示测试数据的组数。对于每组测试数据,第一行包含 24 24 24个整数,分别表示 R ( 0 ) , R ( 1 ) , R ( 2 ) , … , R ( 23 ) R(0),R(1),R(2),…,R(23) R(0),R(1),R(2),,R(23)。第二行包含整数 N N N。接下来 N N N行,每行包含一个整数 t i t_i ti

输出格式:
每组数据输出一个结果,每个结果占一行。如果没有满足需求的安排,输出No Solution

数据范围:
0 ≤ R ( 0 ) ≤ 1000 0≤R(0)≤1000 0R(0)1000
0 ≤ N ≤ 1000 0≤N≤1000 0N1000
0 ≤ t i ≤ 23 0≤t_i≤23 0ti23

先设出每小时有多少个收银员在工作,这样原题就转化为了一系列不等式。设 n i n_i ni表示第 i i i个小时( i i i 1 1 1开始计数,下面也一样)有多少个人来应聘了。在读入了所有的 t i t_i ti的值的时候, n t i + 1 n_{t_i+1} nti+1应当递增 1 1 1,表示 t i + 1 t_i+1 ti+1这个小时的备选人数多了 1 1 1。设 x i x_i xi是第 i i i个小时开始工作的所有应聘的人里,挑了多少个,那么 0 ≤ x i ≤ n i 0\le x_i\le n_i 0xini。此外,要求第 i i i小时总共能提供服务的人数要大于等于 R ( i − 1 ) R(i-1) R(i1),所以有 x i − 7 + x i − 6 + . . . + x i ≥ R ( i − 1 ) x_{i-7}+x_{i-6}+...+x_i\ge R(i-1) xi7+xi6+...+xiR(i1)(这里可以在读入的时候,将 R ( 0 ) R(0) R(0)读成 r 1 r_1 r1,以此类推,以保证下标的一致性)。为了能用差分约束来做,可以用前缀和思想,令 s i = x 0 + . . . + x i − 1 s_i=x_0+...+x_{i-1} si=x0+...+xi1 s 0 = 0 s_0=0 s0=0。这样就有: { 0 ≤ s i − s i − 1 ≤ n i s i − s i − 8 ≥ r i , i ≥ 8 s i + ( s 24 − s i + 16 ) ≥ r i , 0 < i < 7 \begin{cases}0\le s_i-s_{i-1}\le n_i\\s_i-s_{i-8}\ge r_i, i\ge 8\\ s_i+(s_{24}-s_{i+16})\ge r_i, 0<i < 7\end{cases} 0sisi1nisisi8ri,i8si+(s24si+16)ri,0<i<7我们可以枚举 s 24 s_{24} s24,直到上述差分约束有解为止。为了实现这一点,我们要将上面的最后一个不等式拆成三个: s i + ( s 24 − s i + 16 ) ≥ r i , 0 < i < 7 ⇔ { s i − s i + 16 ≥ r i − c , 0 < i < 7 s 24 ≤ s 0 + c s 24 ≥ s 0 + c s_i+(s_{24}-s_{i+16})\ge r_i, 0<i < 7\Leftrightarrow\\\begin{cases} s_i-s_{i+16}\ge r_i-c, 0<i<7\\ s_{24}\le s_0+c \\s_{24}\ge s_0+c \end{cases} si+(s24si+16)ri,0<i<7sisi+16ric,0<i<7s24s0+cs24s0+c这样枚举 c c c就行了。这个问题只需判断某个差分约束是否有解,所以求最短路最长路都是可以的。求最长(短)路的时候,可以从 0 0 0号点开始搜,因为 0 0 0可以到达别的任意点(当然也可以同时从所有点开始搜,即等价于开个超级源点,该点到其余每个点距离都是 0 0 0,然后从超级源点开始搜)。发现存在正环则说明无解,即 c c c不合法,要重新尝试另一个 c c c,否则说明有解。此外,枚举 c c c的过程可以用二分来做,因为如果对于某个人数 a a a来说有解,那么对于大于 a a a的人数一定也是有解的。代码如下:

#include <iostream>
#include <cstring>
using namespace std;

const int N = 30, M = 100;
int n;
int h[N], e[M], ne[M], w[M], idx;
int r[N], num[N];
int dist[N];
int q[N], cnt[N];
bool st[N];

void add(int a, int b, int c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

void build(int c) {
    memset(h, -1, sizeof h);
    idx = 0;

    for (int i = 1; i <= 24; i++) {
        add(i - 1, i, 0);
        add(i, i - 1, -num[i]);
    }

    for (int i = 8; i <= 24; i++) add(i - 8, i, r[i]);
    for (int i = 1; i <= 7; i++) add(i + 16, i, -c + r[i]);

    add(0, 24, c);
    add(24, 0, -c);
}

// spfa找正环
bool spfa(int s24) {
    build(s24);
    memset(st, 0, sizeof st);
    memset(cnt, 0, sizeof cnt);
    memset(dist, 0, sizeof dist);

    int hh = 0, tt = 0;
    for (int i = 0; i <= 24; i++) {
        q[tt++] = i;
        st[i] = true;
    }

    while (hh != tt) {
        int t = q[hh++];
        if (hh == N) hh = 0;
        st[t] = false;
        for (int i = h[t]; ~i; i = ne[i]) {
            int v = e[i];
            if (dist[v] < dist[t] + w[i]) {
                dist[v] = dist[t] + w[i];
                cnt[v] = cnt[t] + 1;
                // 一共25个点,发现正环了则返回false
                if (cnt[v] >= 25) return false;

                if (!st[v]) {
                    q[tt++] = v;
                    if (tt == N) tt = 0;
                    st[v] = true;
                }
            }
        }
    }

    return true;
}

int main() {
    int T;
    cin >> T;
    while (T--) {
        for (int i = 1; i <= 24; i++) cin >> r[i];
        cin >> n;
        memset(num, 0, sizeof num);
        for (int i = 0; i < n; i++) {
            int t;
            cin >> t;
            num[t + 1]++;
        }

		// 二分总人数
        int lo = 0, ri = n;
        while (lo < ri) {
            int mid = lo + (ri - lo >> 1);
            if (spfa(mid)) ri = mid;
            else lo = mid + 1;
        }

        if (!spfa(lo)) puts("No Solution");
        else cout << lo << endl;
    }

    return 0;
}

时间复杂度 O ( m n log ⁡ r ) O(mn\log r) O(mnlogr) n n n m m m分别是图的点数和边数, r r r是搜索区间,空间 O ( n ) O(n) O(n)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在设计开发E洁家政服务平台时,使用了Java语言和Spring Boot框架进行开发。Spring Boot是一个用于构建独立的、生产级别的Spring应用程序的框架,它简化了Spring应用程序的配置和部署过程。通过Spring Boot,可以快速搭建一个基于Java的Web应用程序。\[1\] 另外,您提到了Vue,Vue是一种用于构建用户界面的JavaScript框架。它可以与Spring Boot结合使用,用于开发前端界面。Vue提供了一套简洁、灵活的API,使得开发人员可以轻松地构建交互式的Web界面。\[1\] 在E洁家政服务平台的开发中,Spring Boot用于后端的业务逻辑和数据处理,而Vue可以用于前端的用户界面设计和交互。通过这种组合,可以实现用户在线预订雇佣家政服务人员上门服务的功能。\[1\] 总结起来,Spring Boot和Vue是两个不同的框架,分别用于后端和前端的开发。在E洁家政服务平台的设计中,使用了Spring Boot进行后端开发,同时可以结合Vue进行前端开发,以实现全面的功能和用户体验。 #### 引用[.reference_title] - *1* [【毕业设计/课程设计】基于SpringBoot+VUE的家政服务平台设计与实现(源码+文章) 前后端分离 Java | JSP |...](https://blog.csdn.net/weixin_69135630/article/details/131852840)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值