经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
贪心法求解问题
来源:cnblogs  作者:jzhszy  时间:2023/10/25 13:02:15  对本文有异议

一、背包问题

1.1 问题描述

设有编号为1、2、......、n的n个物品,它们的重量分别为w1、w2、......、wn,价值分别为v1、v2、......、vn,其中wi和vi均为正数,有一个背包可以懈怠的最大重量不超过W。求解目标是在不超过背包附中的前提下使背包装入的总价值最大(即效益最大化)。与0/1背包问题的区别是,这里的每个物品可以取一部分装入背包。

1.2 求解思路

采用贪心法求解。用结构体存储物品的价值、重量和价重比,在输入价值重量后,首先求每个物品的价重比p=v/w,将所有物品按照价重比进行排序,从价重比最大的物品开始遍历,若当前物品的重量小于背包剩余容量wieght,将这个物品全部放入背包中,直到其中一个物品的重量w大于背包剩余容量或者物品放完,若其中一个物品的重量w大于背包剩余容量,就将其一部分装入背包,剩余若还有物品没装,便置为0。

1.3 详细代码

  1. #include<iostream>
  2. #include<algorithm>
  3. using namespace std;
  4. #define MAXV 51
  5. struct NodeType{
  6. double v;//价值
  7. double w;//重量
  8. double p;//价重比
  9. };
  10. int n; //物品件数
  11. double W;//背包限重
  12. NodeType a[MAXV]={{0}};//所有物品
  13. double sumv=0; //当前背包中物品价值
  14. double x[MAXV]={0};//标记每件物品装了多少进背包
  15. void KNap(){
  16. double weight=W;//背包剩余重量
  17. int i;
  18. for(i=0;i<n;i++){
  19. if(a[i].w<weight){//从价重比最大的开始,若能直接装入,就直接装入
  20. x[i]=1;
  21. weight-=a[i].w;
  22. sumv+=a[i].v;
  23. }
  24. else if(a[i].w>weight){//直到其中一个不能完全装入,退出循环
  25. break;
  26. }
  27. }
  28. if(weight>0){//还能继续装入
  29. x[i]=weight/a[i].w; //这件物品装入一部分
  30. sumv+=x[i]*a[i].v;
  31. }
  32. }
  33. bool cmp(NodeType p1,NodeType p2){//排序
  34. return p1.p>p2.p;
  35. }
  36. void Disp(){ //输出
  37. cout<<"W\t"<<"V\t"<<"V/W"<<endl;
  38. for(int i=0;i<n;i++){
  39. cout<<a[i].v<<"\t"<<a[i].w<<"\t"<<a[i].p<<endl;
  40. }
  41. }
  42. void Init(){
  43. cout<<"请输入物品件数:"<<endl;
  44. cin>>n;
  45. cout<<"请输入背包限重:"<<endl;
  46. cin>>W;
  47. cout<<"请输入物品重量和价值:"<<endl;
  48. double v,w;
  49. for(int i=0;i<n;i++){
  50. cout<<"第"<<i+1<<"件物品:";
  51. cin>>w>>v;
  52. a[i].v=v;
  53. a[i].w=w;
  54. a[i].p=v/w;
  55. }
  56. cout<<"排序前:"<<endl;
  57. Disp();
  58. sort(a,a+n,cmp); //排序
  59. cout<<"排序后:"<<endl;
  60. Disp();
  61. KNap();
  62. cout<<"求解结果:"<<endl;
  63. cout<<"x:[ ";
  64. for(int i=0;i<n;i++){
  65. if(i==n-1) cout<<x[i];
  66. else cout<<x[i]<<" , ";
  67. }
  68. cout<<"]"<<endl;
  69. cout<<"总价值:"<<sumv<<endl;
  70. }
  71. int main(){
  72. Init();
  73. return 0;
  74. }

1.4 复杂度分析

排序算法时间复杂度O(nlogn),while循环时间复杂度为O(n),所以本算法的时间复杂度为O(nlogn)。

二、最优装载问题

2.1 问题描述

有n个集装箱要装上一艘载重量为W的轮船,其中集装箱i(1<=i<=n)的重量为wi。不考虑集装箱的体积限制,现要尽可能多的集装箱装上轮船,使他们的重量之和不超过W。

2.2 求解思路

题说要尽可能多的集装箱装上轮船,这里采用贪心法求解,选出尽可能多的集装箱个数。因此,当重量限制为W时,wi越小可装载的集装箱个数越多,所以采用优先选取重量轻的集装箱装船。对于已有的集装箱重量数组w[n],通过sort排序对重量按照从小到大的顺序进行排序,并设置一个标记数组x[n],标记集装箱是否被装入,0表示未被装入,1表示已被装入,船剩余载重量weight船当前载重maxw。遍历数组w,当w[i]<weight时,将其装入,设x[i]=1,weight-=w[i],maxw+=w[i]。直到集装箱被完全装入或剩余集装箱装不进时,退出循环,求解完毕。maxw即为最后轮船上的总重量。

2.3 详细代码

  1. #include<iostream>
  2. #include<algorithm>
  3. using namespace std;
  4. #define MAXV 51
  5. int w[]={0,5,2,4,3};//每个集装箱的重量
  6. int n=5,W=10; // 5个集装箱,船载重为W
  7. int maxw=0; //存放最优解重量
  8. int x[MAXV]={0}; //标记物品是否被存放
  9. bool cmp(int a,int b){
  10. return a<b;
  11. }
  12. void solve(){
  13. int resw=W;
  14. sort(w+1,w+n+1,cmp);//对重量进行排序
  15. for(int i=1;i<=n&&w[i]<=resw;i++){
  16. maxw+=w[i];
  17. x[i]=1;
  18. resw-=w[i];
  19. }
  20. }
  21. int main(){
  22. solve();
  23. cout<<"最优方案为:"<<endl;
  24. for(int i=1;i<=n;i++){
  25. if(x[i]==1) cout<<w[i]<<" ";
  26. }
  27. cout<<endl;
  28. cout<<"最优解重量为:"<<maxw<<endl;
  29. return 0;
  30. }

2.4 时间复杂度分析

快速排序时间复杂度为O(nlogn),遍历重量时间复杂度为O(n),故本算法时间复杂度为O(nlogn)。

三、田忌赛马问题

3.1 问题描述

两千多年前的战国时期,齐威王与大将田忌赛马。双方约定每人各出300匹马,并且在上、中、下三个等级中各选一匹进行比赛,由于齐威王每个等级的马都比田忌的马略强,比赛的结果可想而知。现在双方各n匹马,依次派出一匹马进行比赛,每一轮获胜的一方将从输的一方得到200银币,平局则不用出钱,田忌已知所有马的速度值并可以安排出场顺序,问他如何安排比赛获得的银币最多。输入描述:输入包含多个测试用例,每个测试用例的第一行正整数n(n≤1000)马的数量,后两行分别是n个整数,表示田忌和齐威王的马的速度值。输入n=0结束。输出描述:每个测试用例输出一行,表示田忌获得的最多银币数。

3.2 求解思路

采用贪心算法。令田忌马的速度数组为t[n],左右两侧分别为leftt、rightt,齐威王马的速度数组为q[n],左右两侧分别为leftq,rightq。田忌获得的银币即为monry。田忌赛马问题可以分为以下几种情况:
(1)田忌最快的马比齐威王最快的马快。此时最优解就是两者比赛,田忌赢。此时rightt--,rightq--,money+=200;
(2)田忌最快的马比齐威王最快的马慢。此时最优解是用田忌最慢的马与齐威王最快的马比赛,田忌输。此时leftt++,rightq--,money-=200;
(3)田忌最快的马和齐威王最快的马一样快。此时又分为以下三种情况:
??1、田忌最慢的马比齐威王最慢的马快。此时最优解就是两者比赛,田忌赢。此时leftt++,leftq++,money+=200;
??2、田忌最慢的马比齐威王最慢的马慢。此时最优解就是用田忌最慢的马与齐威王最快的马比赛,田忌输。此时leftt++,rightq--,money-=200;
??3、其他情况。平局。

3.3 详细代码

  1. #include<iostream>
  2. #include<algorithm>
  3. using namespace std;
  4. #define MAXV 1001
  5. int n; //马总数
  6. int t[MAXV]; //田忌
  7. int q[MAXV]; //齐威王
  8. int money=0; //田忌获得的银币总数
  9. void solve(){
  10. sort(t,t+n);//将马的速度按从小到大排序
  11. sort(q,q+n);
  12. int leftt=0;
  13. int leftq=0;
  14. int rightt=n-1;
  15. int rightq=n-1;
  16. while(leftt<=rightt){
  17. if(t[rightt]>q[rightq]){//田忌最快的马齐威王最快的马快,田忌赢
  18. money+=200;
  19. rightt--;
  20. rightq--;
  21. }
  22. else if(t[rightt]<q[rightq]){//田忌最快的马比齐威王最快的马慢
  23. money-=200;
  24. leftt++; //最优解为田忌用最慢的马与齐威王比,田忌输
  25. rightq--;
  26. }
  27. else { ////田忌最快的马与齐威王最快的马速度相同
  28. if(t[leftt]>q[leftq]){//田忌最慢的马比齐威王最慢的马快,田忌赢
  29. money+=200;
  30. leftt++;
  31. leftq++;
  32. }
  33. else if(t[leftt]<q[leftq]){//田忌最慢的马比齐威王最慢的马慢
  34. money-=200;
  35. leftt++; //最优解为田忌最慢的马与齐威王最快的马比,田忌输
  36. rightq--;
  37. }
  38. }
  39. }
  40. }
  41. int main(){
  42. while(true){
  43. cout<<"请输入马的总数:";
  44. cin>>n;
  45. if(n==0) return 0;
  46. cout<<"请输入田忌各匹马的速度:"<<endl;
  47. for(int i=0;i<n;i++){
  48. cin>>t[i];
  49. }
  50. cout<<"请输入齐威王各匹马的速度:"<<endl;
  51. for(int i=0;i<n;i++){
  52. cin>>q[i];
  53. }
  54. solve();
  55. cout<<"田忌能获得的银币最多为:"<<money;
  56. }
  57. return 0;
  58. } `
  59. #### 3.4 时间复杂度分析
  60. &emsp;&emsp;算法主要花费在快速排序,因此时间复杂度为O(nlogn)。
  61. ## 四、多机调度问题
  62. #### 4.1 问题描述
  63. &emsp;&emsp;设有n个独立的作业{1,2,......,n},由m台相同的及其{1,2,......,m}进行加工处理,作业i所需的处理时间为ti(1<=i<=n),每个作业均可在任何一台及其上加工处理,但未完工前不允许中断,任何作业也不能拆分成更小的子作业。多机调度问题要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。
  64. #### 4.2 问题求解
  65. &emsp;&emsp;采用贪心法求解。题说要求在尽可能短的时间内完成,因此分为两种情况,当n<=m时,机器比作业多,可以为每个作业分配一台机器。当n>m时,作业比机器多,此时考虑长作业优先(因为短作业优先时,最开始每个机器上都是短作业,最后分配长作业会发现非常耗时),使用结构体NodeType存储每个作业,包括no(作业编号)、t(时间)、mmo(分配的机器编号)。首先对结构体数组A[n]按照时间从大到小进行排序,先把每个机器上分配一个作业,使用小根堆qu(小根堆会自动排序)来存放结构体,分配下一轮作业时,按照上一轮作业完成时间越小越先出队即e=qu.top(),qu.pop()。出队后将机器分配给下一个作业,并加上时间A[i].t,在将其添加到小根堆中,直到作业分配完毕。
  66. #### 4.3 详细代码
  67. `#include<iostream>
  68. #include<queue>
  69. #include<vector>
  70. #include<algorithm>
  71. using namespace std;
  72. int n=7; //n个作业
  73. int m=3; //m个机器
  74. struct NodeType{
  75. int no;//作业序号
  76. int t;//作业时长
  77. int mmo;//机器序号
  78. bool operator < (const NodeType& s) const{//按t从大到小排序
  79. return t>s.t;
  80. }
  81. };
  82. NodeType A[]={{1,2},{2,14},{3,4},{4,16},{5,6},{6,5},{7,3}};
  83. void solve(){
  84. NodeType e;
  85. if(n<=m){
  86. cout<<"为每一个作业分配一台机器"<<endl;
  87. return ;
  88. }
  89. sort(A,A+n);
  90. priority_queue<NodeType,vector<NodeType>,less<NodeType> > qu;//小根堆,小的元素在上面(自动排序)
  91. for(int i=0;i<m;i++){
  92. A[i].mmo=i+1;
  93. cout<<"给机器"<<i+1<<"分配作业"<<A[i].no<<",执行时间为"<<A[i].t<<",占用时间段[0,"<<A[i].t<<"]"<<endl;
  94. qu.push(A[i]);
  95. }
  96. for(int i=m;i<n;i++){
  97. e=qu.top();
  98. qu.pop();
  99. cout<<"给机器"<<e.mmo<<"分配作业"<<A[i].no<<",执行时间为"<<A[i].t<<",占用时间段["<<e.t<<","<<e.t+A[i].t<<"]"<<endl;
  100. e.t+=A[i].t;
  101. qu.push(e);
  102. }
  103. }
  104. int main(){
  105. cout<<"多机调度问题:"<<endl;
  106. solve();
  107. return 0;
  108. }`
  109. #### 4.4 时间复杂度分析
  110. &emsp;&emsp;算法快速排序时间复杂度O(nlogn),两次for循环时间复杂度共O(n),因此本算法时间复杂度为O(nlogn)。
  111. #### 4.5 小根堆(之前对小根堆了解不多下面记录一下堆)
  112. 1、大根堆:是完全二叉树,其中根结点大于子节点;小根堆:是完全二叉树,其中根结点小于子节点。
  113. 2、c++中,小根堆和大根堆可以使用优先队列实现(priority_queue),优先级高的先出队,自动排序。
  114. `#include<queue>
  115. priority_queue<int> qu;//默认是大根堆
  116. priority_queue<int,vector<int>,greater<int> > qu;//小根堆
  117. priority_queue<int,vector<int>,less<int> > qu;//大根堆
  118. `
  119. 在使用结构体时
  120. `#include<queue>
  121. struct Node{
  122. int x,y;
  123. bool operator < (const Node& a)const{//小的先进堆
  124. return x<a.x;
  125. }
  126. };
  127. priority_queue<Node,vector<Node>,less<Node> > qu;//按照重载后的小于规则,从大到小排序,大的优先级高
  128. `

原文链接:https://www.cnblogs.com/QwertyWang/p/GreedyAlgorithm_1.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号