力(贪心,pair和优先队列)
题目描述
在数轴上,Malnar 从原点(x=0)出发,前往 x=X 处。Malnar 初始有 D 单位能量,每走一个单位长度消耗一单位能量。在整个过程中,能量必须不小于 0。有 n 个餐馆,第 i 个餐馆位于 x=xi 处,在第 i 个餐馆用餐可以使能量增加 yi。至多只能在每个餐馆用一次餐,且不同餐馆的 xi可能相同。
求出为了达成目标,至少需要在多少个餐馆用餐。
输入格式
第一行,三个正整数 n,D,X。
第二行,n 个正整数 x1 ,... ,xn
第三行,n 个正整数 y1, ..., yn
输出格式
如果不可能,输出一行一个 -1。
否则输出一行一个非负整数表示答案。
输入输出样例 #1
输入
5 5 12
3 4 7 8 11
3 2 1 2 1
输出
3
输入输出样例 #2
输入
5 10 40
1 20 30 2 38
7 7 7 7 7
输出
5
输入输出样例 #3
输入
4 5 12
3 6 9 10
2 1 2 2
输出
-1
说明/提示
样例解释
样例1 解释:在第 1,2,4 个餐馆用餐。
数据范围
对于100% 的数据,保证:1≤n≤2×1e5, 1≤ D,X,yi ≤1e9, 1≤xi<X。
题解及思路
思路
首先将所有餐馆按照位置从小到大排序。
对于每个餐馆,计算从当前位置到该餐馆所需的能量,如果当前能量不足以到达该餐馆,则需要选择之前未用餐的餐馆中能量增益最大的进行用餐。
更新当前能量和用餐次数后继续前进到下一个餐馆。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,D,X;
int main(){
cin>>n>>D>>X;
vector<pair<int,int>> v(n); //存储餐馆位置和能量
for(int i=0; i<n; i++) cin>>v[i].first; //first即位置
for(int i=0; i<n; i++) cin>>v[i].second; //second是能量
v.push_back({X,0}); //将最后的位置作为一个能量为0的参观记录
sort(v.begin(),v.end()); //排序,确保位置是升序的
int now=0, res=0; //now是当前的位置,res是用餐数量
priority_queue<int> q; //将各个餐馆的能量存储进一个优先队列,默认最大堆
for(int i=0; i<=n; i++){
D-=v[i].first-now; //移动到当前点时的能量
while(D<0&&!q.empty()){ //若D小于0,开始补充能量,每次选择已经走过的餐馆中
D+=q.top(); //能量最大的食用,并将已食用的餐馆从最大堆中移除
q.pop();
res++; //每食用一次,res值加一
}
if(D<0){ //若已全部食用,D仍然小于0,则无法到达终点
cout<<"-1\n";
return 0;
}
now=v[i].first; //更新位置为当前点的位置
q.push(v[i].second); //将当前点的位置加入队列(方便下次循环使用)
}
cout<<res; //最后输出答案
return 0;
}
相关拓展
一、动态数组
1.基本操作
vector<int> vec; // 创建空vector
vector<int> vec(5); // 创建大小为5的vector,初始值为0
vector<int> vec(5, 10); // 创建大小为5的vector,初始值都为10
vec.push_back(20); // 在末尾添加元素
vec.pop_back(); // 删除末尾元素
vec.size(); // 返回元素数量
vec.empty(); // 判断是否为空
vec.clear(); // 清空vector
2.vector和pair
pair是将两个数据组合成一个单元的模板类
基本使用包括
pair<int, string> p1(1, "apple"); // 创建pair
p1.first = 2; // 访问第一个元素
p1.second = "banana"; // 访问第二个元素
auto p2 = make_pair(3, "orange"); // 使用make_pair创建
vector<pair<int, string>> fruits;
fruits.push_back(make_pair(1, "apple"));
fruits.push_back({2, "banana"}); // C++11初始化方式
// 遍历
for(auto& p : fruits) {
cout << p.first << ": " << p.second << endl;
}
// 排序 - 默认按first排序
sort(fruits.begin(), fruits.end());
// 自定义排序 - 按second排序
sort(fruits.begin(), fruits.end(),
[](const auto& a, const auto& b) {
return a.second < b.second;
});
vector<pair<int,int>>是常见的组合,用于存储成对的数据
本题代码中就运用到了,顺带一提本题代码中的第九行也可以这样写:
for (auto& [x, y] : v) cin >> x;
这样写其实更标准一些,遍历v中的所有元素。
3.其他常见动态数组组合
vector<vector
vector<vector<int>> matrix(3, vector<int>(4)); // 3行4列
matrix[1][2] = 5; // 访问元素
vector
vector<tuple<int, string, double>> items;
items.emplace_back(1, "book", 12.5);
auto& item = items[0];
cout << get<0>(item) << endl; // 访问第一个元素
二.优先队列
队列是一种现金显出的数据结构,其设计目的是保证元素按顺序处理(即无法做到访问中间元素),而优先队列则是按优先顺序输出元素,默认是最大堆。
堆是一颗完全二叉树,最大堆中父节点的值都大于等于子节点的值,根是最大值,最小堆则相反。
自定义最小堆:
priority_queue<int, vector<int>, greater<int>> min_pq;
min_pq.push(3); // 插入元素
min_pq.push(1);
cout << min_pq.top(); // 输出1
补充一个队列和pair的使用。如果是默认最大堆,则使用方法和vector的一样,如果要用到最小堆,使用时是这样的:
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> pq;
自定义优先规则:
struct CompareLength {
bool operator()(const string& a, const string& b) {
return a.length() < b.length(); // 长度大的优先级高
}
};
priority_queue<string, vector<string>, CompareLength> custom_pq;
优先队列的应用场景:
算法应用:
Dijkstra最短路径算法:用优先队列快速获取当前距离最近的节点。
哈夫曼编码:合并频率最小的节点。
任务调度:处理优先级高的任务。
实际问题:
合并多个有序链表。
实时获取数据流中的前 K 大元素。
————————————————
题目背景:译自https://hsin.hr/coci/
contest#4 T2
题目链接:https://hydro.ac/d/cduestc/p/2221
该文章内容已在CSDN上发表过,此处为二次编辑
https://blog.csdn.net/SDSSpJ306/article/details/148100258?spm=1011.2124.3001.6209

浙公网安备 33010602011771号