14天阅读挑战赛
*努力是为了不平庸~
系列文章目录
第一章 算法简介
第二章 贪心算法
目录
2.1贪心算法
百度百科中,贪心算法(greedy algorithm ,又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。说人话就是只顾眼前利益,不从长远做打算。我们经常会听到这些话:“人要活在当下”“看清楚眼前”……贪心算法正是“活在当下,看清楚眼前”的办法,从问题的初始解开始,一步一步地做出当前最好的选择,逐步逼近问题的目标,尽可能地得到最优解,即使达不到最优解,也可以得到最优解的近似解。
贪心算法在解决问题的策略上“目光短浅”,只根据当前已有的信息就做出选择,而且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。换言之,贪心算法并不是从整体最优考虑,它所做出的选择只是在某种意义上的局部最优。贪心算法能得到许多问题的整体最优解或整体最优解的近似解。因此,贪心算法在实际中得到大量的应用。
在贪心算法中,我们需要注意以下几个问题。
- 没有后悔药。一旦做出选择,不可以反悔。we
- 有可能得到的不是最优解,而是最优解的近似解。
- 选择什么样的贪心策略,直接决定算法的好坏。
那么具体怎么去利用贪心算法呢?下面介绍基本流程:选择贪心策略,然后得到局部最优解,然后得到全局最优解。具体流程如下所示:
- 贪心策略:首先要确定贪心策略,选择当前看上去最好的一个方案。例如,挑选苹果,如果你认为个大的是最好的,那你每次都从苹果堆中拿一个最大的,作为局部最优解,贪心策略就是选择当前最大的苹果;如果你认为最红的苹果是最好的,那你每次都从苹果堆中拿一个最红的,贪心策略就是选择当前最红的苹果。因此根据求解目标不同,贪心策略也会不同。
- 局部最优解:根据贪心策略,一步一步地得到局部最优解。例如,第一次选一个最大的苹果放起来,记为 a 1 a_1 a1,第二次再从剩下的苹果堆中选择一个最大的苹果放起来,记为 a 2 a_2 a2,以此类推。
- 全局最优解:把所有的局部最优解合成为原来问题的一个最优解 ( a 1 , a 2 , … ) (a_1,a_2,…) (a1,a2,…)。熟知的冒泡算法就属于贪心算法
最优装载问题、背包问题、会议安排、最短路径、哈夫曼编码、最小生成树都利用了贪心算法的思想,下面进行逐个讲解。
2.2最优装载问题
首先叙述解决流程,形成完整的思考流程。
首先,分析问题并想出算法的设计思路(文字叙述)。
然后,给出图解思路。
之后,编写伪代码。
最后,给出完整代码。
还有一点,分析算法的复杂度,并思考改进思路。
2.2.1问题描述:
有一批集装箱要装上一艘载重量为 c c c的轮船。其中集装箱i的重量为 W i W_i Wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。(意思就是在不超过载重量的情况下最多能装多少)
2.2.2分析问题&设计思路:
最优装载问题可以利用贪心算法去求解,题目中要求的是装尽可能多的集装箱,船的载重量最大为
c
c
c且固定的,那么优先将重量小的物体放进去就可以装载尽可能多的集装箱。这里贪心策略选择的就是先装重量最轻者。就可以从局部最优解得到全局最优解。
设计思路:
- 当载重量为定值 c c c时, W i W_i Wi越小时,可装载的集装箱数量 n n n越大。只要依次选择最小重量集装箱,直到不能再装为止。
- 把 n n n个集装箱的重量从小到大(非递减)排序,然后根据贪心策略尽可能多地选出前 i i i个集装箱,直到不能继续装为止,此时达到最优。
2.2.3图解思路:
假设总载重量
c
c
c为30,集装箱总共有8个,重量分别是4、10、7、11、3、5、14、2 。
图示:
重量W[i] | 4 | 10 | 7 | 11 | 3 | 5 | 14 | 2 |
---|
- 首先给该列表从小到大排序:
重量W[i] | 2 | 3 | 4 | 5 | 7 | 10 | 11 | 14 |
---|
- 按照贪心策略,每次选重量最小的放入( t m p tmp tmp代表集装箱的重量, a n s ans ans代表已装载的集装箱的个数)
i
=
0
i=0
i=0,选择排序后的第1个,装入重量
t
m
p
=
2
tmp=2
tmp=2,不超过载重量30,
a
n
s
=
1
ans =1
ans=1。
i
=
1
i=1
i=1,选择排序后的第2个,装入重量
t
m
p
=
2
+
3
=
5
tmp=2+3=5
tmp=2+3=5,不超过载重量30,$ans =2。
i
=
2
i=2
i=2,选择排序后的第3个,装入重量
t
m
p
=
5
+
4
=
9
tmp=5+4=9
tmp=5+4=9,不超过载重量30,$ans =3。
i
=
3
i=3
i=3,选择排序后的第4个,装入重量
t
m
p
=
9
+
5
=
14
tmp=9+5=14
tmp=9+5=14,不超过载重量30,$ans =4。
i
=
4
i=4
i=4,选择排序后的第5个,装入重量
t
m
p
=
14
+
7
=
21
tmp=14+7=21
tmp=14+7=21,不超过载重量30,$ans =5。
i
=
5
i=5
i=5,选择排序后的第6个,装入重量
t
m
p
=
21
+
10
=
31
tmp=21+10=31
tmp=21+10=31,超过载重量30,算法结束。即放入古董的个数为ans=5个。
2.2.4伪代码:
程序=数据结构+算法,需要首先选择数据结构。很显然这里,数据结构用数组承载每个集装箱的重量即可。
-
数据结构定义
根据算法设计描述,我们用一维数组存储集装箱的重量:double w[N];//一维数组存储古董的重
-
按重量排序可以利用C++中的排序函数
sort
,对古董的重量进行从小到大(非递减)排序。要使用此函数需引入头文件:
#include <algorithm>
语法描述为:
sort (begin,end)//参数begin和end表示一个范围,分别为待排序数组的首地址和尾地址//sort函数默认为升序
在本例中只需要调用sort
函数对集装箱的重量进行从小到大排序:sort(w,w+n); //按古董重量升序排序
-
按照贪心策略找最优解
首先用变量 a n s ans ans记录已经装载的集装箱个数, t m p tmp tmp代表装载到船上的集装箱的重量,两个变量都初始化为0。然后按照重量从小到大排序,依次检查每个集装箱, t m p tmp tmp加上该集装箱的重量,如果小于等于载重量 c c c,则令 a n s + + ans ++ ans++;否则,退出。
double temp=0.0;int ans=0;/ l tmp为已装载到船上的集装箱重量,ans为已装载的集装箱个数
for(int i=0;i<n;i++)
{
tmp+=w[i];
if(tmp<=c)
ans ++;
else
break;
}
2.2.5完整代码:
#include <iostream>
#include <algorithm>
const int N=1000005;
using namespace std;
double w[N];//古董的重量数组
int main ()
{
double c;
int n;
cout<<"请输入载重量c及古董个数n: "<<endl;
cin>>c>>n;
cout<<"请输入每个古董的重量,用空格分开:"<<endl;
for(int i=0;i<n;i++)
{
cin>>w[i];//输入每个物品重量
}
sort (w, w+n) ; //按古董重量升序排序
double temp=0.0;
int ans=0;// tmp为已装载到船上的古董重量,ans为已装载的古董个数
for(int i=0;i<n;i++)
{
temp+=w[i];
if(temp<=c)
ans ++;
else
break;
}
cout<<"能装入的古董最大数量为Ans=";
cout<<ans<<endl;
return 0;
}
运行结果:
2.2.6算法复杂度分析及其改进思路:
未更新
2.3其他可用贪心算法解决的问题(未更新)
其他的可以利用贪心算法解决的问题在此提一嘴,先不详细讲述。