贪心的定义(摘自百度百科)
贪心算法(greedy algorithm,又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。
贪心算法是以局部最优而达到全局最优,可以说贪心算法是短视的,每次只考虑当前状况下最好的选择。
贪心并没有通用的模板和算法思路,大多时候是靠刷题积累。
区间问题
AcWing 905. 区间选点
思路分析:
1. 按照右端点从小到大将区间排序
2. 依次从前往后枚举每个区间:
1 > 若当前区间能覆盖所选点,无需操作
2 > 若当前区间不能覆盖所选点,就选择当前区间的右端点作为新选的点,
同时答案要加一
代码展示:
#include <iostream>
#include <algorithm>using namespace std;const int N = 100010;
struct Edge
{int l, r;bool operator < (const Edge &W)const{return r < W.r;}
}edges[N];
int main()
{int n;cin >> n;for (int i = 0; i < n; i ++){int l, r;cin >> l >> r;edges[i] = {l, r};}sort(edges, edges + n);int res = 0, ed = -2e9;for (int i = 0; i < n; i ++){if (edges[i].l > ed){res ++;ed = edges[i].r;}}cout << res << endl;return 0;
}
AcWing 908. 最大不相交区间数量
代码展示:
#include <iostream>
#include <algorithm>using namespace std;const int N = 100010;
struct Edge
{int l, r;bool operator <(const Edge &W)const{return r < W.r;}
}edges[N];int main()
{int n;cin >> n;for (int i = 0; i < n; i ++){int l, r;cin >> l >> r;edges[i] = {l, r};}sort(edges, edges + n);int res = 0, ed = -2e9;for (int i = 0; i < n; i ++){if (edges[i].l > ed){res ++;ed = edges[i].r;}}cout << res << endl;return 0;
}
AcWing 906. 区间分组
思路分析:
1. 区间按照左端点从小到大排序
2. 用小根堆去存储每组的右端点的最大值
3. 从前往后处理每一个区间:
1 > 若当前区间的左端点小于堆顶,说明当前区间与前面所有组都存在交集,
那么就开一个新的组去存储当前区间
2 > 若当前区间的左端点大于堆顶,说明当前区间和堆顶无交集,
则可以将当前区间添加到堆顶所在组中,
即要更新该组在小根堆中存储的右端点数值
代码展示:
#include <iostream>
#include <algorithm>
#include <queue>using namespace std;const int N = 100010;
//按照区间左端点大小排序
struct Range
{int l, r;bool operator <(const Range &W)const{return l < W.l;}
}edges[N];int main()
{int n;cin >> n;for (int i = 0; i < n; i ++){int l, r;cin >> l >> r;edges[i] = {l, r};}sort(edges, edges + n);priority_queue<int, vector<int>, greater<int>> heap;for (int i = 0; i < n; i ++){//当前枚举的区间auto t = edges[i];//当堆中为空或者与堆顶元素有交集if (heap.empty() || heap.top() >= t.l) heap.push(t.r);else{heap.pop();heap.push(t.r);}}cout << heap.size() << endl;return 0;
}
AcWing 907. 区间覆盖
思路分析:
1. 区间按照左端点从小到大排序
2. 从前往后枚举每个区间(双指针算法)
每次选取能覆盖当前点st并且右端点最大的区间,然后更新st
代码展示:
#include <iostream>
#include <algorithm>using namespace std;const int N = 100010;struct Edge
{int l, r;bool operator <(const Edge &W)const{return l < W.l;}
}edges[N];int main()
{int st, ed;cin >> st >> ed;int n;cin >> n;for (int i = 0; i < n; i ++){int l, r;cin >> l >> r;edges[i] = {l, r};}sort(edges, edges + n);int res = 0;bool flag = false;//找到能覆盖当前点的最靠右的区间,更新当前点for (int i = 0; i < n; i ++){int j = i, r = -2e9;while (j < n && st >= edges[j].l) {r = max(r, edges[j].r);j ++;}if (r < st) {res = -1;break;}res ++;if (r >= ed) {flag = true;break;}st = r;i = j - 1;}if (!flag) res = -1;cout << res << endl;return 0;
}
Huffman树
哈夫曼树定义(摘自百度百科)
给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
哈夫曼树的构造
每次选取最小的两个数作为两个权值相加的节点的子节点,在将该节点与未选取的值再重复操作
以一个样例来模拟这个过程:
AcWing 148. 合并果子
思路分析:
用小根堆来存储权值,然后构造以哈夫曼树的思路得出最终结果
代码展示:
#include <iostream>
#include <algorithm>
#include <queue>using namespace std;priority_queue<int, vector<int>, greater<int>> heap;int main()
{int n;cin >> n;while (n --){int x;cin >> x;heap.push(x);}int res = 0;while (heap.size() > 1){int a = heap.top();heap.pop();int b = heap.top();heap.pop();res += (a + b);heap.push(a + b);}cout << res << endl;return 0;
}
排序不等式
AcWing 913. 排队打水
代码展示:
#include <iostream>
#include <algorithm>using namespace std;const int N = 100010;
typedef long long LL;int a[N];int main()
{int n;cin >> n;for (int i = 0; i < n; i ++) cin >> a[i];sort(a, a + n);LL res = 0;for (int i = 0; i < n; i ++)res += a[i] * (n - i - 1);cout << res << endl;return 0;
}
绝对值不等式
公式:
| |a| - |b| | ≤ | a±b | ≤ |a| + |b|
AcWing 104. 货仓选址
代码展示:
#include <iostream>
#include <algorithm>using namespace std;const int N = 100010;int a[N];int main()
{int n;cin >> n;for (int i = 0; i < n; i ++) cin >> a[i];sort(a, a + n);int res = 0;for (int i = 0; i < n; i ++)res += a[i] - a[i / 2];cout << res << endl;return 0;
}
推公式
AcWing 125. 耍杂技的牛
代码展示:
#include <iostream>
#include <algorithm>using namespace std;const int N = 100010;
typedef pair<int, int> PII;PII cow[N];int main()
{int n;cin >> n;for (int i = 0; i < n; i ++){int w, s;cin >> w >> s;cow[i] = {w + s, w};}sort(cow, cow + n);int res = -2e9, sum = 0;for (int i = 0; i < n; i ++){int w = cow[i].second, s = cow[i].first - w;res = max(res, sum - s);sum += w;}cout << res << endl;return 0;
}