贪心算法入门
先从一题例题来说吧:
输入第一行是箱子数量和最大承受重量,往后得到四行每一行的信息是一箱糖果的价值,以及重量。
输出是最大价值的糖果。
解题思路:
按照礼物的价值与重量比(也就是单位糖果的价值)从大到小选取礼物,对每一个礼物都尽可能的多装,直到装满为止。
#include <iostream> #include <algorithm> using namespace std; const double eps = 1e-6; //记住浮点数之间是怎么比较的 struct Candy { int v, w; //又是用到了重载 bool operator <(const Candy& c) const { return double(v) / w - double(c.v) / c.w > eps; } }candies[110]; int main() { int n, m; cin >> n >> m; for (int i = 0; i < n; ++i) { cin >> candies[i].v >> candies[i].w; } //这个结构体内重载之后这个sort函数就是按照重载的规制来了 sort(candies, candies + n); int totalW = 0; double totalV = 0; for (int i = 0; i < n; i++) { if (totalW + candies[i].w <= m) { totalW += candies[i].w; totalV += candies[i].v; } else { //如果不能整箱拿的话就只能拿缺的部分的占比 totalV += candies[i].v * double(m - totalW) / candies[i].w; break; } } printf("%.1f", totalV); return 0; }
这题是比较基础的贪心法,就是只考虑局部最优解,对后续会产生的影响不考虑。但是这个解法不是什么时候都是对的,一定要进行证明,那怎么证明呢?答案是替换法:
用自己的语言描述一下替换法吧:首先有一个不是按照涉及的贪心方式排序的序列1,一个按照上述方法排序的序列2。只要第一次发现两个元素只要不等的话,一定是ai<bi(可以好好理解一下,因为序列2是按照从大到小排序的)。如果我用bi替换了ai的话很明显ai的价值就增加了。所以发现了矛盾。
记住贪心算法一定要证明算法的正确性,比如这里所使用的贪心算法如果糖果只能整箱拿的话就是错误的。
后面再补一个比较简单的例题;
输入第一行是一共有多少部电影,后面12行是每一部电影的开始和结束时间。
解题思路:首先将电影结束时间从小到大排序,第一步选结束时间最早的那部电影,之后就选和上一部电影不冲突且结束时间最早的电影。
看这个证明真的很死脑细胞的,后面对于这个贪心的应用还不如就试一试,试不出来就算了。
#include<iostream> #include<string> #include<algorithm> #include<math.h> using namespace std; struct ti{ int start; int end; }tm[101]; bool cmp(ti a,ti b){ return (a.end<b.end); //按照结束时间从小到大排序 } int main(){ int n; while(true){ scanf("%d",&n); if(n==0) break; for(int i=0;i<n;i++){ scanf("%d%d",&tm[i].start,&tm[i].end); } //按照结束时间排序 sort(tm,tm+n,cmp); //从结束时间最小的开始选取 int ans=1;//每组重置个数 int endtime=tm[0].end; for(int i=1;i<n;i++){ if(tm[i].start>=endtime){ endtime=tm[i].end; ans++; } } printf("%d\n",ans); } }
这个问题我查了一下还有一个说法是区间调度问题这里推荐一篇博客:
原文链接:https://blog.csdn.net/weixin_44572229/article/details/119299135 这篇博客以这个例题对区间调度做了比较好的总结。