洛谷题解——P1678:烦恼的高考志愿

题目相关

题目链接

洛谷,https://www.luogu.com.cn/problem/P1678

我的OJ,http://47.110.135.197/problem.php?id=4276

题目描述

现有 m 所学校,每所学校预计分数线是 ai。有 n 位学生,估分分别为 bi​。

根据 n 位学生的估分情况,分别给每位学生推荐一所学校,要求学校的预计分数线和学生的估分相差最小(可高可低,毕竟是估分嘛),这个最小值为不满意度。求所有学生不满意度和的最小值。

输入格式

第一行读入两个整数 m,n。m 表示学校数,n 表示学生数。

第二行有 m 个数,表示 m 个学校的预计录取分数。

第三行有 n 个数,表示 n 个学生的估分成绩。

输出格式

一行,为最小的不满度之和。

输入样例

4 3
513 598 567 689
500 600 550

输出样例

32

数据规模

m ≤ 100000

ai​ ≤ 10^6

n ≤ 100000

bi ​≤ 10^6

题目分析

题意分析

不需要分析,跳过。

样例数据分析

根据输入的样例数据,我们知道一共有 4 所学校,学校的录取分数分别为:513、598、567、689。一共有 3 名考生,高考的分数为:500、600、550。

第一个考生的分数为 500,这个分数在所有高校录取分数 513 以下,因此该学生的不满意度为 513-500=13。

第二个考生的分数为 600,这个分数在所有高校录取分数 598 和 689 之间,因此该学生的不满意度有两个,分别为 600-598=2 和 689-600=89,根据题意,我们取最小值。因此该学生的不满意度为 600-598=2。

第三个考生的分数为 550,这个分数在所有高校录取分数 513 和 567 之间,因此该学生的不满意度有两个,分别为 550-513=37 和 567-550=17,根据题意,我们取最小值。因此该学生的不满意度为 567-550=17。

这三个学生的所有不满度合计为:13+2+17=32。

数据规模分析

ai​ ≤ 10^6,bi ​≤ 10^6。因此对应的分数我们使用 int 可以描述。

m ≤ 100000,n ≤ 100000。这样极限数据应该是所有高校录取分数都是 10^6,而考生分数是 0 分。这样对应的最大值为 10^6*10^6=10^12,超过了 int 的范围。所以答案需要用 long long 来描述。

算法思路

从样例数据分析中,我们可以看出,本题的核心就是根据每个学生的分数,找出比这个分数小的录取分数的最大值和比这个分数大的录取分数的最小值。既然是查找问题,我们肯定可以使用二分查找来实现,二分查找的时间复杂度为 O(logn)。最多的数据为 1e5 个,因此可以满足。

1、读入数据。

2、对高校录取分数进行排序。

3、应该是没有必要进行出重,当然去重也是可以的。

4、对每个学生高考分数,在录取分数中查找左下界和右上界。

5、如果左下界不等于右上界,说明高考分数和某个学校的录取分数相同。也就意味着这个学生的不满度为 0。

6、如果左下界等于右上界,说明学生高考分数在两个高校录取分数之间。需要分类讨论。分布右三种情况:一是小于高校录取分数最小值,这个时候 lo=hi=0,那么不满值应该是(高校录取分数最小值 - 高考分数);二是大于高校录取分数最大值,这个时候 lo=hi=m,那么不满值应该是(高考分数 - 高校录取分数最大值);三是在两个高校录取分数线之间,那么不满足应该右有两个,即(高考分数 - 高校录取分数[lo-1])和(高校录取分数[hi] - 高考分数),我们取最小值即可。

AC 参考代码

根据上面的分析,我们知道核心就是找出考生分数在高校录取分数的左下界(hi)和右上界(lo)。

/*
OJ:luogu
题目:P1678 烦恼的高考志愿
地址:https://www.luogu.com.cn/problem/P1678
*/
#include <bits/stdc++.h>

const int MAXM = 1e5+2;
int a[MAXM];//学校分数
const int MAXN = 1e5+2;
int b[MAXM];//学生分数

int main() {
    int n, m;
    scanf("%d%d", &m, &n);

    int i;
    //读入学校分数
    for (i=0; i<m; i++) {
        scanf("%d", &a[i]);
    }
    //读入学生分数
    for (i=0; i<n; i++) {
        scanf("%d", &b[i]);
    }
	
    std::sort(a, a+m);//排序

    //统计
    unsigned long long ans = 0;
    for (i=0; i<n; i++) {
        int lo = std::lower_bound(a, a+m, b[i]) - a;
        int hi = std::upper_bound(a, a+m, b[i]) - a;
        if (lo==hi) {
            //b[i]没有在a[i]中,找最小的
            if (0==lo) {
                ans += (a[lo]-b[i]);
            } else if (hi>=m) {
                ans += (b[i]-a[hi-1]);
            } else {
                ans += std::min(b[i]-a[lo-1], a[hi]-b[i]);
            }
        }        
    }
    printf("%llu\n", ans);

    return 0;
}

代码细节

就是对 hi 和 lo 的值进行分类讨论。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的老周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值