• Day20:算法篇之贪心算法


    一、算法思想基础

    1.五大算法思想:

    ①分治思想
            快排、分组排序、归并排序、二分查找
    ②贪心算法/贪婪算法
            大的问题 归纳成小问题 然后迭代
            1)A星寻路算法
            能且只能做当前看来最优的选择 如此反复 试图得到最终最优解
            缺陷:
                1. 并非一定能得到整体最优解
                2. 每一步都是局部最优

           2)最值思想:
           3)背包问题:
                有一个体积为V的背包 
                有N种物品  体积和价值各不相同
                求价值最高的组合方式

            4)迪杰斯卡拉:求最短路径
                

    ③ 动态规划
    ④ 动态回溯--->递归  n皇后问题
    ⑤ 分支定界

    2.贪心算法的基本思想

     3.贪心算法的常见应用:

            ①背包问题(货物装载问题)

            ②最短路径问题--->dijkstra算法

            ③哈夫曼编码

            ④短作业调度问题

    二、具体问题分析与代码实现:

            1.最值问题最短路径问题--->dijkstra算法

    整个算法的流程的关键--->两个内部的循环

            ①第一个for:访问集合内的点都被标记为了flag=1,

                    所以我们需要在剩下的点中找到起点到当前点距离最短的(贪心),

                            并记录min=dist[j],将这个点标记,k表示获取到的其下标

            ②第二个for:在上一个for完成扩充点的基础上

            (扩充完点之后,最新扩充的这个点的dist from 起点 to 新点 就不会再改变了!!!)

            就要开始遍历剩下的点,寻找是否有更优的路径。

                    关键比较对象:min+map.matrix[k][j]与dist[j]

                            其中min就是dist[k]=起点到k点最短的距离,加上matrix[k][j](即k到j的距离)                                         就是表示起点到j这个点的距离。

    1. #include
    2. #include
    3. #include
    4. #define NO 0xFFFFFF //不连通
    5. #define MAX 10 //不能直接定义太大数组
    6. //简单描述一下图
    7. typedef struct graph
    8. {
    9. char vexs[MAX]; //顶点数组
    10. int vexnum; //顶点数
    11. int arcnum; //边数
    12. int matrix[MAX][MAX]; //权值数组
    13. }GRAPH,*LPGRAPH;
    14. void DijKstra(GRAPH map, int in, int dist[])
    15. {
    16. int i = 0;
    17. int flag[MAX]; //成功获取路径(进入了扩充的顶点范围内)的标记
    18. //求出当前节点到其他节点距离
    19. for (int i = 0; i < map.vexnum; i++)
    20. {
    21. flag[i] = 0;
    22. dist[i] = map.matrix[in][i]; //当前节点到其他节点距离遍历第一行权值数组
    23. }
    24. flag[in] = 1;
    25. dist[in] = 0;
    26. int min;
    27. int k=0;
    28. int j;
    29. //2.扩充顶点
    30. //数组存储顶点从下标0存储
    31. for (i = 1; i < map.vexnum; i++) //i=1本质是扩充第二个顶点
    32. {
    33. min = NO; //不连通的值
    34. /*第一个循环:是用来找到目前未扩充的且入口到其点距离最小的那个点*/
    35. for (j = 1; j < map.vexnum; j++)
    36. {
    37. if (flag[j] == 0 && dist[j] < min)
    38. {
    39. min = dist[j];
    40. k = j; //连通的状态
    41. }
    42. }
    43. flag[k] = 1;//找到后,将其标记为1--->代表已经进入扩充的名单内了。
    44. /*第二个循环:判断剩下没进入+有更加适合的路径的点,更新dist距离数组*/
    45. for (j = 1; j < map.vexnum; j++)
    46. {
    47. if (flag[j] == 0 && (min + map.matrix[k][j]) < dist[j])
    48. {
    49. dist[j] = min + map.matrix[k][j];
    50. }
    51. }
    52. }
    53. printf("\n");
    54. for (int i = 1; i < map.vexnum; i++)
    55. {
    56. printf("最短路径:(%c,%c)=%d\n", map.vexs[in], map.vexs[i], dist[i]);
    57. }
    58. //如何求出最短路径 经过那些节点
    59. }
    60. int main()
    61. {
    62. GRAPH map = { {'1','2','3','4','5'},5,7,
    63. {
    64. {NO,10,NO,30,100},
    65. {NO,NO,50,NO,NO},
    66. {NO,NO,NO,NO,10},
    67. {NO,NO,20,NO,60},
    68. {NO,NO,NO,NO,NO}
    69. }
    70. };
    71. int in = 0; //'1'这个顶点进来,找到其他顶点距离
    72. int dist[MAX];
    73. DijKstra(map, in, dist);
    74. return 0;
    75. }

            2.最值问题

                     Q:求最大值?

    主体:

    1. #include
    2. #define NUM 5
    3. int arr[NUM][NUM] = { 0 };
    4. //临时数组
    5. int maxArr[NUM][NUM] = { 0 };
    6. int count = 0;
    7. //返回 a b 中大的那一个
    8. int Max(int a, int b)
    9. {
    10. return ((a > b) ? a : b);
    11. }
    12. //初始化数组
    13. void initArr();
    14. //获取最大路径
    15. int getMax(int i, int j);
    16. int main()
    17. {
    18. initArr();
    19. int num = getMax(0, 0);
    20. printf("num:%d,count:%d\n", num,count);
    21. while (1);
    22. return 0;
    23. }
    24. //初始化数组
    25. void initArr()
    26. {
    27. arr[0][0] = 9;
    28. arr[1][0] = 4; arr[1][1] = 7;
    29. arr[2][0] = 5; arr[2][1] = 3; arr[2][2] = 1;
    30. arr[3][0] = 2; arr[3][1] = 4; arr[3][2] = 4; arr[3][3] = 1;
    31. arr[4][0] = 7; arr[4][1] = 5; arr[4][2] = 3; arr[4][3] = 2; arr[4][4] = 4;
    32. for (int i = 0; i < NUM; i++)
    33. {
    34. for (int j = 0; j < NUM; j++)
    35. {
    36. maxArr[i][j] = -1;
    37. }
    38. }
    39. }

    核心函数getMax的实现与逐步优化 :

    ①版本一:递归暴力求解。

    (会有重复搜的地方---->左支向下搜索,完毕后,进行右支搜索,会有重复的搜索部分)

    1. //获取最大路径
    2. int getMax(int i, int j)
    3. {
    4. //step1 :递归方式 每一步都计算 爆破法
    5. if (NUM == i) return arr[i][j];//越界 循环结束
    6. int n = getMax(i + 1, j);
    7. int m = getMax(i + 1, j + 1);
    8. count++;//统计计算次数
    9. return arr[i][j] + Max(n, m);
    10. }

    ②版本二:优化上述问题,左支自底向上求解出的max将其存储起来,等到第二次搜索时,若已经标记过就不用再搜索了,直接用就可以。

    1. //获取最大路径
    2. int getMax(int i, int j)
    3. {
    4. //step2 :递归方式 有一些没有意义的递归 要省略 存储之前递归计算出来的结果 直接用 而不是每次都递归计算出来再用
    5. if (maxArr[i][j] != -1) return maxArr[i][j];
    6. /*省略就体现在这一步, 因为标记只会标记较优解(下面走过的路, 已经额外存储过了, 比如左边搜过了, 进行一系列的标记max,
    7. 当右边搜到相同子分支的时候, 就没有必要搜了(自下而上将其求解出最优解了, 没有必要再去搜了))*/
    8. count++;
    9. if (NUM == i)
    10. {
    11. maxArr[i][j] = arr[i][j];
    12. }
    13. else
    14. {
    15. int n = getMax(i + 1, j);
    16. int m = getMax(i + 1, j + 1);
    17. maxArr[i][j] = arr[i][j] + Max(n, m);
    18. }
    19. return maxArr[i][j];
    20. }

    ③版本三:递归写法--->优化成循环写法(减少函数调用的开销

    1. //获取最大路径
    2. int getMax(int i, int j)
    3. {
    4. //step3 :循环方式 直接从下往上加
    5. //先给最下面一层赋值
    6. for (int i = 0; i < NUM; i++)
    7. maxArr[NUM - 1][i] = arr[NUM - 1][i];
    8. //循环 一层层 加 一层层往上赋值
    9. for (int i = NUM - 2; i >= 0; i--)
    10. {//从下往上
    11. for (int j = 0; j <= i; j++)
    12. {
    13. maxArr[i][j] = arr[i][j] + Max(maxArr[i + 1][j], maxArr[i + 1][j + 1]);
    14. }
    15. }
    16. //返回maxArr[0][0]
    17. return maxArr[0][0];
    18. }

    ④版本四:由于要额外开辟一个maxArr用来存储搜索过的最优解,十分耗费空间的,

                    于是可以用覆盖的方式,进行空间层面的优化。

    1. //获取最大路径
    2. int getMax(int i, int j)
    3. {
    4. //step4 :循环方式 直接从下往上加 空间方面只用一行(采用覆盖的方式优化空间)
    5. int temp[NUM];
    6. //先给最下面一层赋值
    7. for (int i = 0; i < NUM; i++)
    8. temp[i] = arr[NUM - 1][i];
    9. //循环 一层层 加 一层层往上赋值
    10. for (int i = NUM - 2; i >= 0; i--)
    11. {//从下往上
    12. for (int j = 0; j <= i; j++)
    13. {
    14. temp[j] = arr[i][j] + Max(temp[j], temp[j + 1]);
    15. }
    16. }
    17. //返回
    18. return temp[0];
    19. }

            3.背包问题

    1. #include
    2. /*
    3. 有N样物品 N:5
    4. 容量为V的背包 V:20
    5. A B C D E
    6. w: 3 5 6 7 9
    7. c: 2 8 7 4 1
    8. 32
    9. */
    10. #define V 20
    11. #define N 5
    12. struct Items
    13. {
    14. int w;//体积weight
    15. int c;//价值
    16. };
    17. Items wp[N] = { { 3, 2}, { 5,8 }, { 6, 7}, { 7,4 }, { 9,1 } };
    18. //返回 a b 中大的那一个
    19. int Max(int a, int b)
    20. {
    21. return ((a > b) ? a : b);
    22. }
    23. int main()
    24. {
    25. int temp[100] = { 0 };//存储各种体积对应的价值
    26. for (int i = 0; i < N; i++)
    27. {//种类搭配
    28. for (int j = wp[i].w; j <= V; j++)
    29. {//j表示体积
    30. temp[j] = Max(temp[j], temp[j - wp[i].w] + wp[i].c);
    31. printf("i:%d,j:%d,temp[%d-%d]:%d,temp[%d]:%d\n",
    32. i, j, j, wp[i].w, temp[j - wp[i].w],j,temp[j]);
    33. }
    34. }
    35. printf("max:%d\n", temp[V]);
    36. return 0;
    37. }

     ①j++表示什么?

           当i=0的时候,表示只可以装第一个物品, 所以这边temp 的变化是这样的(temp[20]=12是因为6个w=3的物品,价值为2,故6*2),当i=1的时候,表示可以在原来的基础上搭配第二个物品->依次更新temp,一直到i=4完成最后一次的更新。

    ②temp[j-wp[i].w]+wp[i].c

            表示如果在原来temp数组的方案商空出i=1的这个item的体积对应的价值再加上i=1的item的价值,去和对应temp[j]价值进行比较--->取较大的temp值进行存储即可。例:当i=2,temp[14]=18表示当上限体积给到14的时候,仅有0和1两种物品,最优价值可以达到18。

            4.哈夫曼编码

     Day14/15:哈夫曼树、哈弗曼编码(压缩与解压缩)__Brooke_的博客-CSDN博客

  • 相关阅读:
    Ubuntu服务器安全性提升:修改SSH默认端口号
    设计模式的一些笔记(个人见解参杂各个模式的用途,未完待续0907)
    【PHP进阶】Rabbitmq的实际使用
    Python语言学习:Python语言学习之面向对象编程OO(继承&封装&多态)/类&方法/装饰器的简介、案例应用之详细攻略
    [SQL Server]在应使用条件的上下文(在 ‘)‘ 附近)中指定了非布尔类型的表达式,查询时间大于某个数值时
    STC51单片机29——汇编语言 取表法 流水灯
    02 Spring_IOC 控制反转
    关于 XXX::getXxx() 如何理解
    如何解决研发数据传输层面安全可控、可追溯的共性需求?
    AI绘画的魅力与未来:人工智能如何重塑艺术创作
  • 原文地址:https://blog.csdn.net/zjjaibc/article/details/126508981