• 【数据结构算法笔记】----贪心算法(简单贪心:月饼问题、最优装箱问题、整数配对、最大组合整数。区间贪心:区间不相交问题、区间选点问题)


    一、什么是贪心算法?

    贪心是用来解决一类最优化问题,并希望由局部最优策略来推得全局最优结果的算法思想。贪心算法适用的问题一定满足最优子结构性质,即一个问题的最优解可以由它的子问题的最优解有效地构造出来。

    玛卡你废话少说,直接上代码吧

    得嘞~

    二、简单贪心

    ①月饼问题

       问题描述:   您是个大老板,手里有一堆月饼。现给定所有种类的月饼的库存量、总售价以及市场的最大需求量,试计算可以获得的最大收益是多少

          注意:销售时允许取出一部分库存。样例给出的情形是这样的: 假如有三种月饼,其库存量分别为18、15、10万吨,总售价分别为75、72、45亿元。如果市场的最大需求量只有(20万吨,那么最大收益策略应该是卖出全部15万吨第二种月饼以及5万吨第三种月饼,获得72+45/2=94.5(亿元)。

     输入格式:每个输入包含1个测试用例。每个测试用例先给出一个不超过1000的正整数N表示月用饼的种类数以及不超过500(以万吨为单位)的正整数D表示市场最大需求量; 随后一行给出N个正数表示每种月饼的库存量(以万吨为单位); 最后一行给出N个正数表示每种月饼的总售价(以亿元为单位)。数字间以空格分隔。
    输出格式:对每组测试用例,在一行中输出最大收益,以亿元为单位精确到小数点后两位。
    输入样例:3   20
                     18  15  10
                     75  72  45
    输出样例:94.50

     分析:步骤1:总是选择单价最高的月饼出售,可以获得最大的利润”的策略。因此,对每种月饼,都根据其库存量和总售价来计算出该种月饼的单价。之后,将所有月饼按单价从高到低排序。
    从单价高的月饼开始枚举。
    ①如果该种月饼的库存量不足以填补所有需求量,则将该种月饼全部卖出,此时需求量减少该种月饼的库存量大小,收益值增加该种月饼的总售价大小。
    ②如果该种月饼的库存量足够供应需求量,则只提供需求量大小的月饼,此时收益值增加当前需求量乘以该种月饼的单价,而需求量减为0。这样,最后得到的收益值即为所求的最大收益值。

    1. #include<stdio.h>
    2. #include<algorithm>
    3. using namespace std;
    4. struct mooncake{
    5. double store ;//库存量
    6. double sell;//总售价
    7. double price;// 单价/
    8. }cake[1010];
    9. bool cmp(mooncake a,mooncake b){//按单价从高到低排序
    10. return a.price>b.price;
    11. }
    12. int main() {
    13. int n;
    14. double D;
    15. scanf("%d%lf",&n,&D);
    16. for(int i=0;i<n;i++){
    17. scanf("%lf",&cake[i].sell);
    18. }
    19. for(int i = 0;i < n;i++) {
    20. scanf("%lf",&cake[i]. sell);
    21. cake[i].price=cake[i].sell/cake[i].store;//计算单
    22. }
    23. sort(cake,cake+n,cmp);//按单价从高到低排序
    24. double ans=0;//收益
    25. for(int i=0;i < n;i++) {
    26. if(cake[i]. store<=D) {//如果需求量高于月饼库存量
    27. D -=cake[i].store; //第i种月饼全部卖出
    28. ans+=cake[i].sell;
    29. }else { //如果月饼库存量高于需求量
    30. ans+=cake[i].price*D;//只卖出剩余需求量的月饼break;
    31. break;
    32. }
    33. }
    34. printf("%.2f\n", ans);
    35. return 0;
    36. }

    ②最优装箱问题

       问题描述:   有n个箱子需要装上一艘轮船,已知第i个箱子的重量为wi,轮船的载重为W。问在不超过轮船载重的前提下,最多能把多少个箱子装上轮船。

     输入格式:

    第一行两个正整数n、W(1≤n≤105、1≤W≤107),分别表示箱子个数和轮船载重。

    第二行n个正整数wi(1≤wi≤107),表示n个箱子的重量。


    输出格式:输出两个整数,分别表示能装上轮船的箱子个数和总重量,用空格隔开。
    输入样例:5 11

                      7 2 9 1 11
    输出样例:3 10

     分析:能将重量分别为7、2、1的箱子装上轮船(此时箱子个数最多),总重量为10。还是先考虑排序。

    1. #include <cstdio>
    2. #include <algorithm>
    3. using namespace std;
    4. const int MAXN = 100010;
    5. int a[MAXN];
    6. int main() {
    7. int n, maxW;
    8. scanf("%d%d", &n, &maxW);
    9. for (int i = 0; i < n; i++) {
    10. scanf("%d", &a[i]);
    11. }
    12. sort(a, a + n);
    13. int num = 0, sum = 0;
    14. for (int i = 0; i < n; i++) {
    15. if (sum + a[i] <= maxW) {
    16. num++;
    17. sum += a[i];
    18. } else {
    19. break;
    20. }
    21. }
    22. printf("%d %d", num, sum);
    23. return 0;
    24. }

    ③整数配对问题

       问题描述:有两个正整数集合S、T,其中S中有n个正整数,T中有m个正整数。定义一次配对操作为:从两个集合中各取出一个数a和b,满足a∈S、b∈T、a≤b,配对的数不能再放回集合。问最多可以进行多少次这样的配对操作。

     输入格式:

    第一行 两个正整数n、m(1≤n≤104、1≤m≤104),分别表示S和T中正整数的个数。

    第二行 n个正整数ai(1≤ai≤105),表示S中的n个正整数。

    第三行 m个正整数bi(1≤bi≤105),表示T中的m个正整数。


    输出格式:输出一个整数,表示最多的配对操作次数。
    输入样例:3 3

                     2 5 3   

                     3 3 4
    输出样例:2

     分析:2与其中一个3配对,3与另一个3配对,5无法和4配对。因此最多配对两次。

    1. #include <cstdio>
    2. #include <algorithm>
    3. using namespace std;
    4. const int MAXN = 10000;
    5. int a[MAXN], b[MAXN];
    6. int main() {
    7. int n, m;
    8. scanf("%d%d", &n, &m);
    9. for (int i = 0; i < n; i++) {
    10. scanf("%d", &a[i]);
    11. }
    12. for (int i = 0; i < m; i++) {
    13. scanf("%d", &b[i]);
    14. }
    15. sort(a, a + n);
    16. sort(b, b + m);
    17. int counter = 0;
    18. int i = 0, j = 0;
    19. while (i < n && j < m) {
    20. if (a[i] <= b[j]) { //在排序好之后,依次比较ab的大小,相同则b再加
    21. counter++; //counter计数的前提是出现a<b的关系
    22. i++;
    23. j++;
    24. } else {
    25. j++;
    26. }
    27. }
    28. printf("%d", counter);
    29. return 0;
    30. }

    ④最大组合整数

       问题描述:现有0~9中各个数的个数,将它们组合成一个整数,求能组合出的最大整数。

     输入格式:

    在一行中依次给出0~9中各个数的个数(所有个数均在0~100之间)。数据保证至少有一个数的个数大于0。


    输出格式:输出一个整数,表示能组合出的最大整数。
    输入样例:1 0 2 0 0 0 0 0 0 1
    输出样例:9220

     分析:存在1个0、2个2、1个9,因此能组合出的最大整数是9220,显然输出的数越靠后越大,肯定要把大的数放前面

    1. #include <cstdio>
    2. #include <algorithm>
    3. using namespace std;
    4. const int MAXN = 10;
    5. int cnt[MAXN];
    6. int main() {
    7. for (int i = 0; i < MAXN; i++) {
    8. scanf("%d", &cnt[i]);
    9. }
    10. bool isZero = true;
    11. for (int i = MAXN - 1; i >= 0; i--) {
    12. if (i > 0 && cnt[i] > 0) { //如果该数字不为空,即有数字
    13. isZero = false;
    14. }
    15. if (!isZero) {
    16. for (int j = 0; j < cnt[i]; j++) { //有数字即输出
    17. printf("%d", i);
    18. }
    19. }
    20. }
    21. if (isZero) {
    22. printf("0");
    23. }
    24. return 0;
    25. }

    三、区间贪心

     ①区间不相交问题

       问题描述:给定n个开区间,从中选择尽可能多的开区间,使得这些开区间两两没有交集。

     输入格式:

    第一行为一个正整数n(1≤n≤104),表示开区间的个数。

    接下来n行,每行两个正整数xi、yi(0≤xi≤105、0≤yi≤105),分别表示第i个开区间的左端点和右端点。


    输出格式:输出一个整数,表示最多选择的开区间个数。​​​​​​​
    输入样例:4

                      1 3

                      2 4

                      3 5

                      6 7
    输出样例:3

     分析: 先将区间左端点进行排序,某段选右端点小于下一段的左端点,即不相交,再把下一段的左端点改成该段的右端点,最多选择(1,3)、(3,5)、(6,7)三个区间,它们互相没有交集。

    1. #include <cstdio>
    2. #include <algorithm>
    3. using namespace std;
    4. const int MAXN = 10000;
    5. struct Interval {
    6. int l, r;
    7. } interval[MAXN];
    8. bool cmp(Interval a, Interval b) {
    9. if (a.l != b.l) {
    10. return a.l > b.l;
    11. } else {
    12. return a.r < b.r;
    13. }
    14. }
    15. int main() {
    16. int n;
    17. scanf("%d", &n);
    18. for (int i = 0; i < n; i++) {
    19. scanf("%d%d", &interval[i].l, &interval[i].r);
    20. }
    21. sort(interval, interval + n, cmp);
    22. int num = 1, lastL = interval[0].l;
    23. for (int i = 1; i < n; i++) {
    24. if (interval[i].r <= lastL) {
    25. lastL = interval[i].l;
    26. num++;
    27. }
    28. }
    29. printf("%d", num);
    30. return 0;
    31. }

      ②区间选点问题

       问题描述:给定n个闭区间,问最少需要确定多少个点,才能使每个闭区间中都至少存在一个点。

     输入格式:

    第一行为一个正整数n(1≤n≤104),表示闭区间的个数。

    接下来n行,每行两个正整数xi、yi(0≤xi≤105、0≤yi≤105),分别表示第i个闭区间的左端点和右端点。


    输出格式:输出一个整数,表示最少需要确定的点的个数。​​​​​​​
    输入样例:3

                      1 4

                       2 6

                       5 7
    输出样例: 2

     分析: 求相交的部分,至少需要两个点(例如3和5)才能保证每个闭区间内都有至少一个点。

    1. #include <cstdio>
    2. #include <algorithm>
    3. using namespace std;
    4. const int MAXN = 10000;
    5. struct Interval {
    6. int l, r;
    7. } interval[MAXN];
    8. bool cmp(Interval a, Interval b) {
    9. if (a.l != b.l) {
    10. return a.l > b.l;
    11. } else {
    12. return a.r < b.r;
    13. }
    14. }
    15. int main() {
    16. int n;
    17. scanf("%d", &n);
    18. for (int i = 0; i < n; i++) {
    19. scanf("%d%d", &interval[i].l, &interval[i].r);
    20. }
    21. sort(interval, interval + n, cmp);
    22. int num = 1, lastL = interval[0].l;
    23. for (int i = 1; i < n; i++) {
    24. if (interval[i].r < lastL) {//如果两区间相交,则左右端点融合
    25. lastL = interval[i].l;
    26. num++;
    27. }
    28. }
    29. printf("%d", num);
    30. return 0;
    31. }

    总的来说,就是自己先想好什么策略最优,然后去验证可行性,勇敢去想吧!

  • 相关阅读:
    Misc思路
    王道考研计算机网络——传输层
    u-boot 编译与运行
    Nodejs
    Amazon云计算AWS(四)
    尚硅谷-Spring Cloud
    Docker
    Spark - 权威指南
    webGL编程指南 第三章 平移三角形 TranslatedTriangle.js
    AWTK 支持可独立安装的小应用程序 (applet)
  • 原文地址:https://blog.csdn.net/weixin_51538341/article/details/125448623