话说回来很久没上来写过博客了,包括去年和前年广东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个子目录4和5,总的大小为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卷】