期末考试 (洛谷P3745)

[六省联考 2017] 期末考试

题目描述

n n n 位同学,每位同学都参加了全部的 m m m 门课程的期末考试,都在焦急的等待成绩的公布。

i i i 位同学希望在第 t i t_i ti 天或之前得知所有课程的成绩。如果在第 t i t_i ti 天,有至少一门课程的成绩没有公布,他就会等待最后公布成绩的课程公布成绩,每等待一天就会产生 C C C 不愉快度。

对于第 i i i 门课程,按照原本的计划,会在第 b i b_i bi 天公布成绩。

有如下两种操作可以调整公布成绩的时间:

  1. 将负责课程 X X X 的部分老师调整到课程 Y Y Y,调整之后公布课程 X X X 成绩的时间推迟一天,公布课程 Y Y Y 成绩的时间提前一天;每次操作产生 A A A 不愉快度。
  2. 增加一部分老师负责学科 Z Z Z,这将导致学科 Z Z Z 的出成绩时间提前一天;每次操作产生 B B B 不愉快度。

上面两种操作中的参数 X , Y , Z X, Y, Z X,Y,Z 均可任意指定,每种操作均可以执行多次,每次执行时都可以重新指定参数。

现在希望你通过合理的操作,使得最后总的不愉快度之和最小,输出最小的不愉快度之和即可。

输入格式

第一行三个非负整数 A , B , C A, B, C A,B,C,描述三种不愉快度,详见【题目描述】;
第二行两个正整数 n , m n, m n,m,分别表示学生的数量和课程的数量;
第三行 n n n 个正整数 t i t_i ti,表示每个学生希望的公布成绩的时间;
第四行 m m m 个正整数 b i b_i bi,表示按照原本的计划,每门课程公布成绩的时间。

输出格式

输出一行一个整数,表示最小的不愉快度之和。

样例 #1

样例输入 #1

100 100 2
4 5
5 1 2 3
1 1 2 3 3

样例输出 #1

6

样例 #2

样例输入 #2

3 5 4
5 6
1 1 4 7 8
2 3 3 1 8 2

样例输出 #2

33

提示

样例解释 1

由于调整操作产生的不愉快度太大,所以在本例中最好的方案是不进行调整;全部的 5 5 5 门课程中,最慢的在第 3 3 3 天出成绩;
同学 1 1 1 希望在第 5 5 5 天或之前出成绩,所以不会产生不愉快度;
同学 2 2 2 希望在第 1 1 1 天或之前出成绩,产生的不愉快度为 ( 3 − 1 ) × 2 = 4 (3 - 1) \times 2 = 4 (31)×2=4
同学 3 3 3 希望在第 2 2 2 天或之前出成绩,产生的不愉快度为 ( 3 − 2 ) × 2 = 2 (3 - 2) \times 2 = 2 (32)×2=2
同学 4 4 4 希望在第 3 3 3 天或之前出成绩,所以不会产生不愉快度;
不愉快度之和为 4 + 2 = 6 4 + 2 = 6 4+2=6

数据范围

Case # n , m , t i , b i n, m, t_i, b_i n,m,ti,bi A , B , C A, B, C A,B,C
1, 2 1 ≤ n , m , t i , b i ≤ 2000 1 \leq n, m, t_i, b_i \leq 2000 1n,m,ti,bi2000 A = 1 0 9 ; B = 1 0 9 ; 0 ≤ C ≤ 1 0 2 A = 10^9; B = 10^9; 0 \leq C \leq 10^2 A=109;B=109;0C102
3, 4 1 ≤ n , m , t i , b i ≤ 2000 1 \leq n, m, t_i, b_i \leq 2000 1n,m,ti,bi2000 0 ≤ A ; C ≤ 1 0 2 ; B = 1 0 9 0 \leq A; C \leq 10^2; B = 10^9 0A;C102;B=109
5, 6, 7, 8 1 ≤ n , m , t i , b i ≤ 2000 1 \leq n, m, t_i, b_i \leq 2000 1n,m,ti,bi2000 0 ≤ B ≤ A ≤ 1 0 2 ; 0 ≤ C ≤ 1 0 2 0 \leq B \leq A \leq 10^2; 0 \leq C \leq 10^2 0BA102;0C102
9 - 12 1 ≤ n , m , t i , b i ≤ 2000 1 \leq n, m, t_i, b_i \leq 2000 1n,m,ti,bi2000 0 ≤ A , B , C ≤ 1 0 2 0 \leq A, B, C \leq 10^2 0A,B,C102
13, 14 1 ≤ n , m , t i , b i ≤ 1 0 5 1 \leq n, m, t_i, b_i \leq 10^5 1n,m,ti,bi105 0 ≤ A , B ≤ 1 0 5 ; C = 1 0 16 0 \leq A, B \leq 10^5; C = 10^{16} 0A,B105;C=1016
15 - 20 1 ≤ n , m , t i , b i ≤ 1 0 5 1 \leq n, m, t_i, b_i \leq 10^5 1n,m,ti,bi105 0 ≤ A , B , C ≤ 1 0 5 0 \leq A, B, C \leq 10^5 0A,B,C105

思路

程序所求的不愉快度是一个关于公布天数的函数,该函数是一个下凹函数,即它有最小值,该程序让我们求这个最小值。
由以上分析,我们可以对该题使用三分法求最小值,但这时需要注意区间为整数,故循环的退出条件应该为区间长度小于等于 2 2 2
区间的右端点就是预计公布日期的最大值,可以在每次读取预计公布日期的时候来更新最大值。
计算不愉快度的函数可以分为部分(这是因为这样做方便了一个特例),一部分计算通过实施操作 1 和操作 2 所产生的不愉快度,另一部分计算公布成绩日期太迟导致学生们产生的不愉快度。

特例

C 的值为 1016 使,就算需要将 105 门课程的公布成绩日期调整到第一天最多也才产生 105 * 105 = 1010 的不愉快度,故此时必须在学生中希望日期中的最早日期公布所有成绩,为了寻找这个最早日期,可以在每次输入时都进行更新最早日期。

第一部分

操作 1 会使提前一天公布某科成绩,并且使推迟一天公布某科成绩;操作 2 只会使提前一天公布某科成绩。
如果设定最迟日期 day 公布成绩,则需要根据两种操作产生的不愉快度 A, B 来决定该如何安排这两种操作。
如果 A < B ,则应该尽量多使用操作 1 ,剩下的还需要提前的天数就用操作 2 补齐;否则应该全部使用 2 ,因为如果使用了 x 次操作 1 ,则会多增加 x * (a - b) 的不愉快度。

第二部分

直接用学生希望的日期与日期 day 做比较,如果希望的日期早于 day ,则增加 day 与希望日期的差值 乘 C 的不愉快度。

代码

#include <iostream>
using std::cin, std::cout;
long long A, B, C, ans = 1e16; // 分别为三种不愉快度
int n, m; // n 为学生数量、 m 为课程数量
const int Lim = 100005;
int t[Lim], b[Lim];
int Min(int a, int b) // 用于返回两个 int 类型的值的最小值
{
    return (a < b ? a : b);
}
int Max(int a, int b) // 用于返回两个 int 类型的值的最大值
{
    return (a > b ? a : b);
}
long long Min(long long a, long long b) // 用于返回两个 long long 类型的值的最大值
{
    if (a < b)
        return a;
    else
        return b;
}
long long calculate_AB(int day) // 计算通过操作 A、B 把时间调到 day 的不愉快度
{
    // ahead 和 putoff 分别是把原先所有的公布时间 提前 和 推迟 调整到 day 的总天数
    long long ahead = 0LL, putoff = 0LL;
    for (int i = 0; i < m; i++)
    {
        if (b[i] > day)    ahead += b[i] - day;
        else             putoff += day - b[i];
    }
    if (A < B) // 如果操作 1 产生的不愉快度比操作 2 小
        return Min(ahead, putoff) * A + (ahead - Min(ahead, putoff)) * B; // 尽量用操作 1
    else
        return ahead * B; // 否则直接全用操作 2
}
long long calculate_C(int day) // 计算公布日期为 day 导致学生们等待的不愉快度
{
    long long sum = 0LL;
    for (int i = 0; i < n; i++)
        if (t[i] < day)
            sum += (day - t[i]) * C;
    return sum;
}
int main()
{
    cin >> A >> B >> C >> n >> m;
    int earliest = 1000000; // 记录学生最早希望公布成绩的日期
    int latest = 0; // 记录按计划最晚公布成绩的日期

    for (int i = 0; i < n; i++)
    {
        cin >> t[i];
        earliest = Min(earliest, t[i]); // 更新最早日期
    }
    for (int i = 0; i < m; i++)
    {
        cin >> b[i];
        latest = Max(latest, b[i]); // 更新最晚日期
    }
    
    if (C >= 1e16) // 如果 C 太大,就在最早希望日期前公布所有成绩
    {
        cout << calculate_AB(earliest);
        return 0;
    }

    // 分别为区间左端点、区间右端点、左三等分点、右三等分点
    int left = 1, right = latest, midL, midR;
    while (right - left > 2)
    {
        midL = left + (right - left) / 3;   midR = right - (right - left) / 3;
        // 如果左三等分点所对应的不愉快度 小于 右三等分点的不愉快度
        if (calculate_AB(midL) + calculate_C(midL) > calculate_AB(midR) + calculate_C(midR))
            left = midL; // 则让子区间的左端点取左三等分点
        else
            right = midR; // 否则让子区间的右端点取右三等分点
    }
    long long res; // 用于存储计算出来的不愉快度
    for (int i = left; i <= right; i++) // 在最后的小区间中枚举出最小值
    {
        res = calculate_AB(i) + calculate_C(i);
        ans = Min(ans, res); // 更新最小值
    }
    cout << ans;
    return 0;
}
  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值