[BalticOI 2019 Day2] 汤姆的餐厅

[BalticOI 2019 Day2] 汤姆的餐厅

题目背景

译自 BalticOI 2019 Day2 T1. Tom's Kitchen

题目描述

Tom's Kitchen 是一家非常受欢迎的餐厅,其受欢迎的原因之一是每份菜都由至少 K 名厨师进行准备。今天有 N 份菜需要准备,第 i 份菜的准备时间是 Ai 小时。

Tom 可以雇佣 M 名厨师,厨师 j 最多可以工作 Bj 小时。此外,即使厨师 j 的工作时间不到 Bj 小时,他也会得到工作 Bj 小时的报酬。一名厨师可以花不同的时间准备不同的菜,但是每一道菜都需要由至少 K 名厨师准备,并且他们花的时间总和恰好为 Ai。当一名厨师准备菜品时,他总会花正整数小时。

Tom 需要一套最优的雇佣厨师方案,以使得厨师不工作就获得报酬的小时数(即所有雇佣厨师的总工作时间上限与准备所有菜的总时间之差)尽可能小。

输入格式

第一行包含三个正整数:N,M,K

第二行包含 N 个整数 Ai,第三行包含 M 个整数 Bj

输出格式

如果不存在一套方案可以完成所有菜的准备工作,输出 Impossible。否则输出一个整数,代表厨师不工作就获得报酬的小时数的最小值。

样例 #1

样例输入 #1

1 2 2
5
3 4

样例输出 #1

2

样例 #2

样例输入 #2

1 1 2
5
5

样例输出 #2

Impossible

样例 #3

样例输入 #3

3 3 3
3 3 2
3 3 3

样例输出 #3

Impossible

提示

样例解释 1

Tom 需要雇佣这两位厨师来完成所有菜的准备工作。准备所有菜的时间为 5 小时,但 Tom 需要支付 3+4=7 小时的费用,即厨师不工作就能得到 2 小时的工资。

样例解释 2

准备一份菜需要至少两位厨师,但只能雇佣一位厨师,因此不能完成准备工作。

样例解释 3

第三份菜无法由三位厨师来准备,因为准备第三份菜的时间只有 2 小时(这意味着如果由三名厨师准备第三份菜的话,至少有一位厨师的准备时间是 0 小时,而这是不符合题目要求的)。

子任务

各子任务的分值与数据规模如下:

  • 子任务 1(9 分):1N,K300,1M2,1Ai,Bj300
  • 子任务 2(22 分):1N,K300,1M15,1Ai,Bj300
  • 子任务 3(20 分):1N,M,Ai,Bj300,K=1
  • 子任务 4(21 分):1N,M,K,Ai,Bj40
  • 子任务 5(28 分):1N,M,K,Ai,Bj300

 

解题思路

  参考题目提供者写的题解,已经写得非常好了。

  首先如果存在某个 ai<k 显然无解,因为做这道菜的厨师至少消耗 1 单位时间,而这道菜又至少需要 k 个厨师,因此必须有 aik

  假设选择的厨师的集合是 S,若这个方案合法首先必须有 iSbii=1nai,同时还要保证每道菜都至少有 k 个厨师参与。这个感觉挺难想到如何去表示的,也不是很理解。

  由于每道菜至少有 k 个厨师参与,因此把一道菜的时间分成 k 和剩余的 aik,那么参与这道菜的厨师最多可以贡献 1 单位时间(对于 k 这部分而言),共至少需要 n×k 这么多的贡献。而一个厨师最多可以贡献  min{bi,n} 个单位的时间(即最多可以参与这么多不同的菜),因此需要满足 iSmin{bi,n}n×k

  考虑 dp,定义状态 f(i,j) 表示从前 i 个厨师中选出总工作时间恰好为 j 的所有方案中贡献的最大值。根据是否选择第 i 个厨师进行状态划分(01 背包问题),有状态转移方程 f(i,j)=max{f(i1,j),f(i1,jbi)+min{bi,n}}

  最终答案就是从满足 i=1naiji=1mbij 中,找到满足 f(m,j)n×k 的最小的 j

  AC 代码如下,时间复杂度为 O(mbi)

#include <bits/stdc++.h>
using namespace std;

const int N = 310;

int a[N], b[N];
int f[N * N];

int main() {
    int n, m, k;
    scanf("%d %d %d", &n, &m, &k);
    int sa = 0, sb = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
        sa += a[i];
    }
    for (int i = 1; i <= m; i++) {
        scanf("%d", b + i);
        sb += b[i];
    }
    for (int i = 1; i <= n; i++) {
        if (a[i] < k) {
            printf("Impossible");
            return 0;
        }
    }
    memset(f, -0x3f, sizeof(f));
    f[0] = 0;
    for (int i = 1; i <= m; i++) {
        for (int j = sb; j >= b[i]; j--) {
            f[j] = max(f[j], f[j - b[i]] + min(b[i], n));
        }
    }
    int ret = 1e9;
    for (int i = sa; i <= sb; i++) {
        if (f[i] >= n * k) ret = min(ret, i - sa);
    }
    if (ret == 1e9) printf("Impossible");
    else printf("%d", ret);
    
    return 0;
}

 

参考资料

  题解 P6228 【[BalticOI 2019 Day2]汤姆的餐厅】:https://www.luogu.com.cn/blog/StudyingFather/solution-p6228

posted @   onlyblues  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示