前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >信奥赛-刷题笔记-二分篇-T2-P1678烦恼的高考志愿0528

信奥赛-刷题笔记-二分篇-T2-P1678烦恼的高考志愿0528

原创
作者头像
IT从业者张某某
修改2025-05-28 09:44:39
修改2025-05-28 09:44:39
6300
代码可运行
举报
运行总次数:0
代码可运行

总题单

本部分总题单如下

【腾讯文档】副本-CSP-JS+NOI 题单 (未完待续)

https://docs.qq.com/sheet/DSmJuVXR4RUNVWWhW?tab=BB08J2

二分篇题单

P1678 烦恼的高考志愿

https://www.luogu.com.cn/problem/P1678

题目背景

计算机竞赛小组的神牛 V 神终于结束了高考,然而作为班长的他还不能闲下来,班主任老 t 给了他一个艰巨的任务:帮同学找出最合理的大学填报方案。可是 v 神太忙了,身后还有一群朋友等着和聚餐,于是他想到了同为计算机竞赛小组的你,请你帮他完成这个艰巨的任务。

题目描述

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

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

输入格式

第一行读入两个整数 m,n

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

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

输出格式

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

输入输出样例 #1

输入 #1

代码语言:txt
复制
4 3
513 598 567 689
500 600 550

输出 #1

代码语言:txt
复制
32

说明/提示

数据范围:

对于 30\% 的数据,1\leq n,m\leq10^3 ,估分和录取线 \leq10^4

对于 100\% 的数据,1\leq n,m\leq10^5 ,估分和录取线 \leq 10^6 且均为非负整数。

代码1-暴力

代码语言:cpp
代码运行次数:0
运行
复制
#include<bits/stdc++.h>

using namespace std;
int m,n;//m个学校 n个学生 
int school[100100],students[100100];
int main(){
//	freopen("C:\\Users\\zhangjun\\Downloads\\P1678_1.in","r",stdin);
//	freopen("C:\\Users\\zhangjun\\Downloads\\P1678_1.inOUT","w",stdout);
	cin>>m>>n;
	for(int i=1;i<=m;i++){
		scanf("%d",&school[i]);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&students[i]);
	}
//	for(int i=1;i<=m;i++){
//		printf("%d ",school[i]);
//	}
//	cout<<endl;
//	for(int i=1;i<=n;i++){
//		printf("%d ",students[i]);
//	}	
	// 累加器
	int  sum=0; 
	// 遍历每个学生 
	for(int i=1;i<=n;i++){
		// 完成学生与每个学校分数的比较,
		// 并找出最小值
		int minDifference=9999999;
		// 遍历每个学校 
		for(int j=1;j<=m;j++){
			if(minDifference>abs(students[i]-school[j])) {
				minDifference=abs(students[i]-school[j]);
			} 
		} 
		sum+=minDifference;
	} 
	cout<<sum;
	return 0;
}

代码2-二分

代码语言:cpp
代码运行次数:0
运行
复制
#include <iostream>
#include <cmath>
#include <algorithm> // 提供 sort 函数
using namespace std;

// 数组 a 存储学校分数线(最多 100100 个)
// 数组 b 存储学生估分成绩(最多 100100 个)
int a[100100], b[100100];

int main() {
    int n, m; // n: 学校数量, m: 学生数量
    cin >> n >> m;

    // 输入每个学校的分数线
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }

    // 输入每个学生的估分
    for (int i = 1; i <= m; ++i) {
        cin >> b[i];
    }

    // 将学校分数线从小到大排序
    sort(a + 1, a + n + 1);

    int ans = 0; // 不满意度总和

    // 遍历每位学生
    for (int i = 1; i <= m; ++i) {
        int target = b[i]; // 当前学生的估分

        // 设置初始搜索范围 [l, r)
        int l = 1, r = n + 1;

        // 找第一个 > target 的位置
        while (l < r) {
            int mid = (l + r) / 2;
            if (a[mid] <= target) {
                l = mid + 1; // 如果当前值小于等于目标,说明要往右找更大的
            } else {
                r = mid;     // 否则往左缩
            }
        }

        // 此时 l 是第一个 > target 的位置
        // 最接近的两个是:a[l-1] 和 a[l]

        // 特判:如果所有学校分数都比 target 大
        if (target <= a[1]) {
            ans += a[1] - target;
        } else {
            // 比较 a[l-1] 和 a[l] 哪个更近
            ans += min(abs(a[l - 1] - target), abs(a[l] - target));
        }
    }

    // 输出最终结果
    cout << ans << endl;

    return 0;
}

代码3-完美答案

代码语言:cpp
代码运行次数:0
运行
复制
#include <iostream>   // 输入输出流
#include <cmath>      // 提供 abs() 等数学函数
#include <algorithm>  // 提供 sort(), lower_bound() 等算法函数
using namespace std;

// 定义最大数据范围:1e5 + 10,避免数组越界
const int MAXN = 1e5 + 10;

// a 数组存储学校分数线
// b 数组存储学生估分成绩
int a[MAXN], b[MAXN];

int main() {
    // n: 学校数量, m: 学生数量
    int n, m;
    cin >> n >> m;

    // 读取每个学校的分数线到数组 a 中
    for (int i = 0; i < n; ++i)
        cin >> a[i];

    // 对 a 数组进行排序,方便后面使用二分查找
    sort(a, a + n);

    // ans 记录所有学生的不满意度总和
    long long ans = 0;

    // 遍历每个学生
    for (int i = 0; i < m; ++i) {
        // 读取当前学生的估分成绩
        cin >> b[i];

        // 使用 lower_bound 查找第一个 **大于等于** 当前学生成绩 b[i] 的位置
        // 返回的是一个指针,指向找到的位置
        int* it = lower_bound(a, a + n, b[i]);

        // 初始化差值为一个非常大的数
        int diff = 2e9;

        // 如果 it 没有越界(即不是 a + n),说明找到了 >= b[i] 的元素
        if (it != a + n)
            diff = abs(*it - b[i]);  // 计算与这个学校的差值

        // 如果 it 不是数组开头(即不是 a),说明存在前面那个更小的元素
        if (it != a)
            diff = min(diff, abs(*(it - 1) - b[i]));  // 和前一个比较,取最小值

        // 将当前学生的不满意度加入总和
        ans += diff;
    }

    // 输出最终结果
    cout << ans << endl;

    return 0;
}

✅ 二、关于 lower_bound 的详细介绍

📌 函数原型:
代码语言:cpp
代码运行次数:0
运行
复制
template<class ForwardIt, class T>
ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value);
  • 功能:在有序区间 [first, last) 中查找第一个 不小于(>=)value 的元素。
  • 返回值:返回一个迭代器或指针,指向该元素;如果没有找到,则返回 last

🧠 工作原理

以数组为例:

代码语言:cpp
代码运行次数:0
运行
复制
int arr[] = {1, 3, 5, 7, 9};
int* it = lower_bound(arr, arr + 5, 6);
  • {1, 3, 5, 7, 9} 中查找第一个 >= 6 的元素;
  • 找到的是 7,它在索引 3;
  • 所以 it == &arr[3]

⚠️ 注意事项
  1. 必须保证数组有序(升序):
    • lower_bound 是基于二分查找实现的,只适用于有序数组;
    • 否则行为未定义。
  2. 返回的是指针
    • 如果你操作的是数组,比如 a,那么 it 是一个 int* 类型;
    • 可以通过 *it 获取值,也可以通过 it - a 得到下标(注意类型匹配)。
  3. 边界处理很重要
    • 如果找不到(如所有元素都比目标小),it == a + n,此时不能访问 *it
    • 要做判断防止越界访问。

现场真题注意事项

https://cspoj.com/contest.php?cid=1002Fus5yz4x3EcSJH1Z

注意事项

文件名(程序名和输入输出文件名)必须使用英文小写。(提交必须使用freopen()进行提交)

C/C++ 中函数 main() 的返回值类型必须是 int,程序正常结束时的返回值必须是0。

提交的程序代码文件的放置位置请参考各省的具体要求。

因违反以上三点而出现的错误或问题,申述时一律不予受理。

若无特殊说明,结果的比较方式为全文比较(过滤行末空格及文末回车)。

程序可使用的栈空间内存限制与题目的内存限制一致。

全国统一评测时采用的机器配置为:Inter® Core™ i7-8700K CPU @3.70GHz,内存 32GB。上述时限以此配置为准。

只提供 Linux 格式附加样例文件。

评测在当前最新公布的 NOI Linux 下进行,各语言的编译器版本以此为准

假设输入样例数据存在文件test.in中,输出样例数据存在文件test.out中,

则在CSP、NOI等比赛的代码中,需添加freopen、fclose语句,

内容详见模板代码如下。

代码语言:cpp
代码运行次数:0
运行
复制
#include <bits/stdc++.h>
#include<cstdio>//必须包含cstdio头文件
#include<iostream>
using namespace std;
 
int main(){
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	
	cout<<"Hello NOI"<<endl;
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}

复制

下面为函数的简介,详细可参见 http://www.cplusplus.com/reference/clibrary/cstdio/freopen.html

函数名:freopen

声明:

代码语言:cpp
代码运行次数:0
运行
复制
FILE _freopen( const char_ path, const char _mode, FILE_ stream );

所在文件: stdio.h

参数说明:

path: 文件名,用于存储输入输出的自定义文件名。

mode: 文件打开的模式。和fopen中的模式(如r-只读, w-写)相同。

stream: 一个文件,通常使用标准流文件。

返回值:成功,则返回一个path所指定文件的指针;失败,返回NULL。(一般可以不使用它的返回值)

功能:实现重定向,把预定义的标准流文件定向到由path指定的文件中。标准流文件具体是指stdin、stdout和stderr。其中stdin是标准输入流,默认为键盘;stdout是标准输出流,默认为屏幕;stderr是标准错误流,一般把屏幕设为默认。通过调用freopen,就可以修改标准流文件的默认值,实现重定向。

代码语言:cpp
代码运行次数:0
运行
复制
#include<iostream>
#include<cstdio>
using namespace std;
int main(){
    freopen("7532.in", "r", stdin);
    freopen("7532.out", "w", stdout);
    //原来的代码保持不变
    double a, b, r;
    int k;
    cin >> a >> b;
    k = int(a/b);
    r = a - b * k;
    printf("%g", r);
    //-------------
    fclose(stdin);
    fclose(stdout);
    return 0;
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 总题单
  • 二分篇题单
  • P1678 烦恼的高考志愿
    • 题目背景
    • 题目描述
    • 输入格式
    • 输出格式
    • 输入输出样例 #1
      • 输入 #1
      • 输出 #1
    • 说明/提示
    • 代码1-暴力
    • 代码2-二分
    • 代码3-完美答案
      • ✅ 二、关于 lower_bound 的详细介绍
  • 现场真题注意事项
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档