Loading

「学习笔记」贪心

贪心算法(英语:\(\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;
}
posted @ 2023-03-01 22:22  yi_fan0305  阅读(20)  评论(0编辑  收藏  举报