贪心算法经典问题
目录
贪心思想
一、Dijkstra最短路问题
问题描述:
贪心策略:
二、Prim 和 Kruskal 最小生成树问题
Prim 算法:
Kruskal 算法:
三、Huffman树问题
问题描述:
贪心策略:
四、背包问题
问题描述:
贪心策略:
五、硬币找零问题
问题描述:
贪心策略:
六、区间合并问题
问题描述:
贪心策略:
七、选择不相交区间问题
问题描述:
贪心策略:
八、区间选点问题
问题描述
贪心策略
九、区间覆盖问题
问题描述:
贪心策略:
十、区间分组问题
问题描述:
贪心策略:
十一、任务调度问题
问题描述:
贪心策略:
十二、加油站问题
问题描述:
贪心策略:
十三、跳跃游戏
问题描述:
贪心策略:
十四、跳跃游戏 II
问题描述:
贪心策略:
十五、股票买卖问题
问题描述:
贪心策略:
十六、最小代价贪心问题
问题描述:
贪心策略:
1.https://codeforces.com/contest/2118/problem/C
思路:
代码:
2.https://codeforces.com/contest/1722/problem/D
思路:
代码:
十七、其他
1.B-黄_牛客小白月赛118
思路:
代码:
2.https://codeforces.com/problemset/problem/2116/B
思路及代码:cf2116B-CSDN博客
3.https://codeforces.com/problemset/problem/2067/B
思路及代码:cf2067B-CSDN博客
4.https://codeforces.com/problemset/problem/2059/B
思路及代码:cf2059B-CSDN博客
5.https://codeforces.com/contest/1760/problem/E
思路:
代码:
贪心思想
贪心算法(Greedy Algorithm)是一种在求解问题时,每一步都选择当前最优解,以期望最终得到全局最优解的算法思想。贪心算法的基本思想可以总结为“每一步都做出一个局部最优的选择,最终就能得到全局最优解”。
一、Dijkstra最短路问题
问题描述:
在一个图中,找到从起点到其他所有点的最短路径(适用于无负边权的情况)。
贪心策略:
每次扩展离源点最近的未访问节点。
二、Prim 和 Kruskal 最小生成树问题
Prim 算法:
从任意一个顶点开始,逐步加入与当前树连接权重最小的边。
Kruskal 算法:
按边权从小到大排序,依次加入不会形成环的边。
三、Huffman树问题
问题描述:
构造一个带权路径长度最小的二叉树,用于数据压缩中的最优前缀编码。
贪心策略:
每次合并两个频率最小的节点。
四、背包问题
问题描述:
物品可以取部分,目标是在容量限制下最大化总价值。
贪心策略:
按单位重量价值从高到低依次选取。
五、硬币找零问题
问题描述:
给定不同面值的硬币,求最少数量凑出某个金额。
贪心策略:
优先使用最大面值的硬币。
六、区间合并问题
问题描述:
给定多个区间,合并所有有重叠或相邻的区间,返回合并后的无重叠区间列表。
贪心策略:
按左端点排序后依次处理,若当前区间与结果中最后一个区间重叠,则合并。
七、选择不相交区间问题
问题描述:
给定多个时间区间,从中选择尽可能多的互不重叠的区间。
贪心策略:
按右端点从小到大排序,每次选择最早结束的区间,跳过与其冲突的区间。
八、区间选点问题
问题描述
在每个区间中至少选一个点,使得这些点覆盖所有区间,要求所选点的数量最少。
贪心策略
按右端点排序,每次在当前未被覆盖的区间的右端点上选一个点。
九、区间覆盖问题
问题描述:
给定一个目标区间和若干子区间,判断是否能用这些子区间完全覆盖目标区间。
贪心策略:
按左端点排序,从起点开始,每次选择能延伸最远的区间,逐步扩展覆盖范围。
十、区间分组问题
问题描述:
将一组区间分成若干个组,每组内的区间互不重叠,要求使用的组数最少。
贪心策略:
按左端点排序,使用最小堆维护各组的最早可用时间,优先将当前区间分配给最早空闲的组。
十一、任务调度问题
问题描述:
给定若干任务及其完成期限和利润,选择能获得最大利润的任务序列。
贪心策略:
按利润降序排序,依次尝试安排任务在最后可行的时间点。
十二、加油站问题
问题描述:
一辆车绕一圈油路,判断是否可以从某一点出发完成一圈,并找出该起点。
贪心策略:
维护当前油量和总油量,若当前油量为负则重新设置起点。
十三、跳跃游戏
问题描述:
数组中每个元素代表可跳跃的最大步数,判断是否可以到达最后一个位置。
贪心策略:
维护当前能跳到的最远位置,逐步推进。
十四、跳跃游戏 II
问题描述:
在跳跃游戏中,求最少跳跃次数到达终点。
贪心策略:
维护当前能跳的最远边界,以及下一步的最远可达。
十五、股票买卖问题
问题描述:
允许多次买卖,但每次只能持有一股。
贪心策略:
只要第二天价格比今天高,就买入卖出。
十六、最小代价贪心问题
问题描述:
在有限操作次数内,通过优先选择代价最低的操作使目标值最大化。
贪心策略:
按操作代价从小到大依次选择操作,直到操作次数用尽。
1.https://codeforces.com/contest/2118/problem/C
思路:
统计每个数字每一位变1的代价,排序取最小的 n 个即可。
代码:
void solve()
{ll n, k;cin >> n >> k;vector<ll> a(n + 10);cin >> a;vector<ll> v;ll ans = 0;for (ll i = 0; i < n; ++i){ll x = a[i];ll cnt = 0;for (ll j = 0; j < 63; ++j){if (x >> j & 1ll)ans++;elsev.pb(1ll << j);}}sort(all(v));for (auto x : v){if (k <= 0)break;if (k >= x){ans++;k -= x;}}cout << ans << endl;
}
2.https://codeforces.com/contest/1722/problem/D
思路:
统计每个字符最大贡献与当前贡献的差值,排序即可。
代码:
void solve()
{int n;string s;cin >> n >> s;s = ' ' + s;ll ans = 0;vector<int> maxx(n + 10, 0);for (int i = 1; i <= n; ++i){if (s[i] == 'L'){maxx[i] = {abs(max(i - 1, (n - i)) - (i - 1))};ans += i - 1;}else{maxx[i] = {abs(max(i - 1, (n - i)) - (n - i))};ans += n - i;}}sort(maxx.begin() + 1, maxx.begin() + 1 + n, greater<int>());for (int i = 1; i <= n; ++i){ans += maxx[i];cout << ans << ' ';}cout << endl;
}
十七、其他
这类题目不是经典的贪心模型,但是解题思路是基于贪心策略的题目。
1.B-黄_牛客小白月赛118
思路:
每次配对 a[i] 和 a[i + 1],如果不互质,就将 a[i + 1] 变为 1,因为 a[i] 在之前可能与其他的元素配对了,选择修改 a[i + 1] 是更优的。
代码:
void solve()
{int n;cin >> n;vector<ll> a(n + 10);for (int i = 0; i < n; ++i)cin >> a[i];ll ans = 0;for (int i = 0; i < n - 1; ++i){if (__gcd(a[i], a[i + 1]) != 1){a[i + 1] = 1;++ans;}}cout << ans << endl;
}
2.https://codeforces.com/problemset/problem/2116/B
思路及代码:cf2116B-CSDN博客
3.https://codeforces.com/problemset/problem/2067/B
思路及代码:cf2067B-CSDN博客
4.https://codeforces.com/problemset/problem/2059/B
思路及代码:cf2059B-CSDN博客
5.https://codeforces.com/contest/1760/problem/E
思路:
题目要求统计 且
的索引对数,由于只有 0、1 两种元素,所以只需统计 1 后面有几个 0 即可;我们还可以将 1 个元素取反,不难发现将第一个值为 0 的元素变 1 或最后一个值为1 的元素变 0 贡献最大,可证明该贪心策略成立,由于数据量不大,直接把三种情况都统计一遍即可(不变的也要统计)。
代码:
void solve()
{int n;cin >> n;vi a(n + 10), b;cin >> a;vi cnt1(n + 10, 0);int cur = 0;for (int i = n - 1; ~i; --i)if (a[i] == 0)++cur;elsecnt1[i] = cur;b = a;for (int i = 0; i < n; ++i)if (b[i] == 0){b[i] = 1;break;}vi cnt2(n + 10, 0);cur = 0;for (int i = n - 1; ~i; --i)if (b[i] == 0)++cur;elsecnt2[i] = cur;b = a;for (int i = n - 1; ~i; --i)if (b[i] == 1){b[i] = 0;break;}vi cnt3(n + 10, 0);cur = 0;for (int i = n - 1; ~i; --i)if (b[i] == 0)++cur;elsecnt3[i] = cur;ll ans1 = 0, ans2 = 0, ans3 = 0;for (int i = 0; i < n; ++i)ans1 += cnt1[i];for (int i = 0; i < n; ++i)ans2 += cnt2[i];for (int i = 0; i < n; ++i)ans3 += cnt3[i];cout << max({ans1, ans2, ans3}) << endl;
}