「学习笔记」贪心
贪心算法(英语:\(\text{greedy algorithm}\)),是用计算机来模拟一个「贪心」的人做出决策的过程。这个人十分贪婪,每一步行动总是按某种指标选取最优的操作。而且他目光短浅,总是只看眼前,并不考虑以后可能造成的影响,可想而知,并不是所有的时候贪心法都能获得最优解,所以一般使用贪心法的时候,都要确保自己能证明其正确性。——\(\text{OI Wiki}\)
对于贪心和动态规划的区别,举一个简单的例子,现在,你眼前有两种选择,玩或者学习,如果是贪心的话,就会选择玩,然后会很快乐,而动态规划会考虑长远,选择学习,在一直往后,贪心会一直选择玩,动态规划会一直选择学习,最后,嗯,可想而知了,贪心虽然快乐,但是一直贪心就会一直快乐!他最终的答案不一定对
当然,贪心也有优点,由于贪心只考虑目前最优的情况,不考虑回溯,所以,它时间效率高!
常见的贪心题型有排序贪心、取最大/最小值贪心
前者为离线处理再选择,后者为在线处理,边处理边选择
题目
River Jumping
贪心思路:先判断是否有解,如果第一块石头离河岸的距离小于 \(s\),或者最后一块石头离河岸的距离小于 \(s\),那么就一定无解;对于连续的三块石头,如果第一块石头到第三块石头之间的距离小于 \(s\),则无解(三块石头最多能跳到两块)
如果有解,那就让它跳到最近的石头上,这就是贪心的思路
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
ll n, m, s;
ll w[N];
bool vis[N];
int main() {
scanf("%lld%lld%lld", &n, &m, &s);
for (int i = 1; i <= m; ++ i) {
scanf("%lld", &w[i]);
}
w[m + 1] = n;
sort(w + 1, w + m + 2);
if (w[1] < s || w[m + 1] < s) {
puts("NO");
return 0;
}
for (int i = 1; i <= m; ++ i) {
if (w[i + 1] - w[i - 1] < s) {
puts("NO");
return 0;
}
}
puts("YES");
int p = 0;
for (int i = 1; i <= m + 1; ++ i) {
if (w[i] - w[p] >= s) {
p = i;
vis[p] = 1;
printf("%d ", p);
}
}
for (int i = m; i >= 0; -- i) {
if (w[p] - w[i] >= s && !vis[i]) {
p = i;
vis[p] = 1;
printf("%d ", p);
}
}
return 0;
}
反悔贪心
顾名思义,可以反悔的贪心,一般用堆来维护,我们来看一道题
[USACO09OPEN]Work Scheduling G
思路:有一个小根堆维护价值,按照时间把任务排序,如果这个任务的时间大于堆中的元素(感谢每个任务的完成时间只有 \(1\)),直接把这个任务的价值加入即可,如果这个任务的时间小于等于堆中的元素,并且,它的价值比堆中最小的元素要大,那我们就进行反悔操作,把最小的元素 pop
掉,把当前任务的价值加入到堆中,同时答案也要更新,这就是反悔操作。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
const int N = 1e5 + 5;
int n;
ll ans;
pll task[N];
priority_queue<ll, vector<ll>, greater<ll> > q;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++ i) {
ll d, p;
scanf("%lld%lld", &d, &p);
task[i] = make_pair(d, p);
}
sort(task + 1, task + n + 1);
for (int i = 1; i <= n; ++ i) {
if (task[i].first > (int)q.size()) {
q.push(task[i].second);
ans += task[i].second;
}
else {
if (task[i].second > q.top()) { // 反悔
ans -= q.top();
q.pop();
q.push(task[i].second);
ans += task[i].second;
}
}
}
printf("%lld\n", ans);
return 0;
}