贪心¶
引入¶
贪心算法是用计算机来模拟我们人类在面对问题时做出决策的过程的一种算法。并且,它在进行决策的时候总是目光短浅地选择最优的操作,来尽可能地取得最优解。
并不是所有的题目都可以由贪心实现,但是贪心是众多算法的基础。比如搜索中的“最优化剪枝”还有 Dijkstra 算法的中心思想,里面都有一些贪心的元素。
贪心算法往往比较简单,因为人类本身就是贪心的。就比如接下来的一道题:
例题
给定
这个问题,就跟你做作业一样,选择一定数量的作业,那么我们每一次都选择量最少的作业,那么最后选择的作业加起来一定也是最少的。本题也一样,每一次都选择最大的那一个数,那么加起来一定也是最多的。
注意这里可能会有负数,但是加负数是没有用的。那么如果我们加到了负数,就说明后面的数都是负数了,那么我们就不加,直接退出即可。
代码
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
sort(a + 1, a + n + 1, greater<>());
for (int i = 1; i <= m; i++) {
ans += a[i] * (a[i] > 0);
}
return ans;
例题¶
排队接水
现在有
首先,看到这题,我们的人脑很快就可以想出一个解法:叫打水更快的人站到前面不就行了,这样子后面的人等的不叫少了吗?可是,这个解法怎么证明是对的呢?
我们可以设总的打水时间为
我们发现,
代码
struct Node {
int v, id;
};
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i].v;
a[i].id = i;
}
sort(a + 1, a + n + 1, [](const Node &a, const Node &b) {
return a.v < b.v;
});
for (int i = 1; i <= n; i++) {
cout << a[i].id << ' ';
sum += a[i].v * (n - i + 1);
}
cout << sum / n << '\n';
合并果子
现在有
首先,看到这题道题目,相信你和我一样,一下子就想到了每一次都合并最轻的两堆果子。但是这样子求解到底对不对呢?
很显然,合并了一堆果子之后,虽然看起来他们已经成为了一堆果子,但是底层上还是分开起来相加了。我们一共需要合并
既然这个算法是对的,那么我们每一次合并了之后都排序,然后把最小的合并,最后就可以算出答案来……了吗?这题显然
其实我们可以使用堆这个数据结构。它可以以
代码
priority_queue<int, vector<int>, greater<>> q; // C++11 之前需要在尖括号之间空一格
cin >> n;
for (int i = 1, x; i <= n; i++) {
cin >> x;
q.push(x);
}
for (int a, b; q.size() > 1; q.push(a + b)) {
a = q.top(), q.pop();
b = q.top(), q.pop();
ans += a + b;
}
cout << ans << '\n';