【2023-8】华为OD机试B卷 - C++考后笔记

文章分享了在华为OD机试中的三个编程题目:时间推送要求找到满足特定条件的数对;文件目录大小计算涉及目录结构和大小累加;数据最节约备份方法关注如何分配文件到光盘保持最少数量。作者详细解释了题目描述、输入输出和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

话说回来很久没上来写过博客了,包括去年和前年广东CCPC都没把做出来的题做记录,这次参加了华为的OD机试,难得重拾兴趣就记录一下吧


一、时间推送

题目描述

同一个数轴X上有两个点的集合 A={A1, A2, …, Am} 和 B={B1, B2, …, Bn},Ai和Bj均为正整数,A、B已经按照从小到大排好序,A、B均不为空,给定一个距离R(正整数),列出同时满足如下条件的所有(Ai, Bj)数对:

1)Ai<= Bj //注意,是A的集合中元素个数比B的小,并不是其中的元素都要小于B中的元素
2)Ai, Bj之间的距离小于等于R
3)在满足1) 2)的情况下,每个Ai只需输出距离最近的Bj
4)输出结果按Ai从小到大的顺序排序

输入描述

第一行三个正整数m,n,R

第二行m个正整数,表示集合A

第三行n个正整数,表示集合B

输入限制

1<=R<=100000, 1<=n,m<=100000, 1<=Ai,Bj<=1000000000

输出描述

每组数对输出一行Ai和Bj,以空格隔开

示例1 (输入输出示例仅供调试,后台判题数据一般不包含示例)

输入

4 5 5
1 5 5 10
1 3 8 8 20

输出

1 1
5 8
5 8

实现代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=1e8;
//题目给的范围是1e9,但是华为的oj最大只能取到1e8,1e9会报错而且似乎是编译器错误,当时没有时间细究
int m,n,r,A[maxn],B[maxn];
int main()
{
	cin >> m >> n >>r;
	vector <int> a;
	vector <int> b;
	for(int i = 0; i < m; i++)
		cin >>A[i];
	for(int j = 0; j < n; j++)
		cin >> B[i];
	for(int i = 0;i < m; i++)
	{
		int min = 0;
		int flag = 0;
		int min_temp = 1e6;
		for(int j = 0; j < n; j++)
		{
			if(A[i] <= B[j] && B[j]-A[i] <= r) //满足1)2)条件
			{ 
				if(j<= n-1 && flag == 0) //此前未存在已寻最短路径且现有可能未遍历
				   min = B[j] - A[i];
				else if(B[j]-A[i] < min) //遍历时若A中的元素比B的大
				   min = A[i] - B[j];
				flag++;//每寻到一个符合条件的记录就记录一次
				if(flag != 0 && min_temp != min) 
				{
					a.push_back(A[i]);//按A[i]大小排序
					b.push_back(min);
					//按距离Bj最近的排序,注意这里排序的是符合小于r的条件下的所有距离
					min_temp = min;
				}
			}
		}
	}
	vector<int>::iterator dis = b.begin();
	for(vector<int>::iterator ans = a.begin(); ans != a.end(); ans++)
	{
		cout << *ans << "  " << *ans+*dis <<endl; 
		//这里的ans是A中最后选取的元素集合,而当A[i]+dis得到的是对应的B[j]
		ans1++;
	}
	return 0;
}

题记

这道题最后的通过率是90%,根据我多年ACM的经历多半是因为数据范围没取满1e9,但是至于为什么1e8能运行但是1e9会报错无法编译,我怀疑是华为OJ的问题,然后说我通过90%的测试用例的原因是后面的运行超时,总之很奇怪,大体思路上其实是没问题的

二、文件目录大小

题目描述

一个文件目录的数据格式为:目录id,本目录中文件大小,(子目录id列表)
其中目录id全局唯一,取值范围[1, 200],本目录中文件大小范围[1, 1000],子目录id列表个数[0,10]例如 : 1 20 (2,3) 表示目录1中文件总大小是20,有两个子目录,id分别是2和3
现在输入一个文件系统中所有目录信息,以及待查询的目录 id ,返回这个目录和及该目录所有子目录的大小之和。

输入描述

第一行为两个数字M,N,分别表示目录的个数和待查询的目录id,

1 ≤ M ≤ 100

1 ≤ N ≤ 200

接下来M行,每行为1个目录的数据:

目录id 本目录中文件大小 (子目录id列表)

子目录列表中的子目录id以逗号分隔。

输出描述

待查询目录及其子目录的大小之和

测试用例1:

输入:

3 1

3 15 ()

1 20 (2)

2 10 (3)

输出:45

说明:目录1大小为20,包含一个子目录2 (大小为10),子目录2包含一个子目录3(大小为15),总的大小为20+10+15=45

测试用例2:

输入:

4 2

4 20 ()

5 30 ()

2 10 (4,5)

1 40 ()

输出:60

说明:目录2包含2个子目录45,总的大小为10+20+30 = 60

实现代码

#include <iostream>
#include <vector>
#include <cstdlib>
#include <cstring>
using namespace std;
int main()
{
    vector<int> size(1000);// 目录 i 的文件大小
    vector<vector<int>> doc(1000);// 目录 i 的所有一级子目录
    int m, n, id;
    string str;
    cin >> m >> n;//定义文件目录数组,子目录数组
    for (int i = 0; i < m; i++) 
    {
        cin >> id >> size[i] >> str;
        int len = str.size();
        for (int j = 0; j < len; j++) 
        {
            // 如果不是数字,说明是分隔符
            if (!isdigit(str[j])) 
                continue;
            int flag = j, temp = 0;
            while (flag < len && isdigit(str[flag])) //寻找括号内的所有数字
            {
                temp = temp * 10 + (str[flag] - '0');//提取子目录号
                flag++;
            }
            doc[id].emplace_back(temp);
            // temp是其中一个子目录,由id指向temp,逐级构造出目录树
            j = flag - 1;//对应前面的flag++,定位当前目录
        }
    }
​
    function<int(int)> dfs = [&](int uid) 
    {
        // 初始化为当前目录的大小
        int ans = size[uid];
        for (int total_size: doc[uid]) 
        {
            // 获取所有子目录的大小
            ans += dfs(total_size);
        }
        return res;
    };// 利用递归求出该目录树的大小
    cout << dfs(n) << endl;//输出答案
    return 0}

题记

这道题并不难,核心思想就是dfs,要说哪里还有坑估计就是算法结构上的问题吧,尽量避免超时

三、数据最节约的备份方法

题目描述

有若干个文件,使用刻录光盘的方式进行备份,假设每张光盘的容量是500MB,求使用光盘最少的文件分布方式
所有文件的大小都是整数的MB,且不超过500MB;文件不能分割、分卷打包

输入描述

一组文件大小的数据

输出描述

使用光盘的数量

备注
不用考虑输入数据不合法的情况;假设最多100个输入文件。

示例一

输入:

100,500,300,200,400

输出:

3

说明 (100,400),(200,300),(500) 3张光盘即可。

输入和输出内容都不含空格。

示例二

输入:

100,100,200,300

输出:

2

实现代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool dfs(vector<int>& cd_size, vector<int>& doc, int index) 
{
    if (index == doc.size()) //当前条件满足
        return true;
    int temp = doc[index];//当前索引指向
    for (int i = 0; i < cd_size.size(); i++) 
    {
        if (i > 0 && cd_size[i] == cd_size[i - 1])
            continue;
        if (cd_size[i] + temp <= 500) 
        {
            cd_size[i] += temp;//单张光盘最终收容
            if (dfs(cd_size, doc, index + 1))//继续寻找是否有符合条件的光盘
                return true;
            cd_size[i] -= temp; // 回溯
        }
    }
    return false;
}
bool check(int count, vector<int>& doc) 
{
    vector<int> cd_size(count, 0);
    return dfs(cd_size, doc, 0);//碟数,各文件大小,索引
}
int main() 
{
    vector<int> doc;//文件
    int num;
    while (cin >> num) 
    {
        doc.push_back(num);
        getchar();
    }
    sort(doc.rbegin(), doc.rend());//排序文件大小
    int min = 1;
    int max = doc.size();
    int cd = max;//最坏情况一个文件一张碟
    while (min <= max) //通过遍历寻找可能答案,利用二分查找缩减时间花费
    {
        int mid = (min + max) >> 1;//进行二分查找
        if (check(mid, doc)) //假定当前所需光盘数,检验是否可行
        {
            cd = mid;
            max = mid - 1;
            //若查找值小于中间值则要查找的值在左边,最小范围不变,最大范围缩小一半
        } 
        else 
            min = mid + 1;
            //若查找的值大于中间值则要查找的值在右边,最大范围不变,最小范围缩小一半
    }
    cout << cd << endl;
    return 0;
}

题记

当时的OD机试限时两个小时,前面两道题为一卷最后一题为二卷,脑子不是很清醒以为一个卷是两个小时就慢慢悠悠地做还反复检验(--||)。。。。。最后看到这道题的时候只剩30秒了无奈错失200分(--||)。。。。核心思想其实和上道题一样还是深搜,但是操蛋的地方在于他有时间限制,事后用过及其暴力的简单方法试过了,当测试用例开始多起来之后是会超时的,所以要考虑预处理+二分查找来降低耗时维度,这道题事后自己解的时候参考了一位大神的思路【塔子哥学算法:备战秋招 每日一题2023.05-B卷】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值