贪心算法概念
根据局部最优解得到问题的整体最优解或整体最优解的近似解:只根据当前已有信息做当前最优选择(且选择了就不能改变)
能利用贪心算法解决的问题有2个特性
1.贪心选择
原问题的整体最优解可以通过一步一步的局部最优选择得到;
2.最优子结构
问题的最优解包含其子问题的最优解;
例如:北京-广州的最短路径问题(必须经过上海),则北京-上海、上海-广州的最短路径是问题的子问题,这个问题的最优解就必须考虑子问题北京-上海、上海-广州的最短路径的最优解
使用贪心算法的步骤
1.确定贪心策略(根据当前信息确定)
2.得到局部最优解
3.合成整体最优解
例子1——最优装载问题
问题描述:海盗船载重为load,每件宝贝的重量为wi,海盗如何把尽可能多的宝贝装上海盗船?
贪心策略:每次选当前重量最轻的宝贝放上海盗船
算法设计:1.对宝贝重量进行排序;sort()函数接收2个参数或者3个参数,可对数组或者vector排序,2个参数分别为首指针和尾后指针,第3个参数可以是一个谓词,头文件<algorithm>
2.按照贪心策略每次选当前最轻的宝贝,直到海盗船装不下,设置计数器,最后计数器的值就是最后装入海盗船的宝贝数量
例子2——部分的背包问题
问题描述:
背包载重为load,山洞中有n个宝贝,每个宝贝的重量为wi,价值为vi,单个宝贝可以分割,盗贼能盗取的最大价值是多少?
贪心策略:
因为单个宝贝可以分割,所以可以引入性价比,每次选当前性价比最高的宝贝放入背包
算法设计:
1.定义一个结构体,包含宝贝的重量、价值、性价比
2.按照贪心策略每次选当前性价比最高的宝贝,直到背包装不下完整宝贝,此时,将性价比最高的那个宝贝进行分割,装入分割宝贝,使背包刚好装满
注意:可分割的情况,所有数据请用double表示
代码实现:
1 struct item { 2 double weight; 3 double value; 4 double ratio; 5 }; 6 7 bool cmp(item& a, item& b) 8 { 9 return a.ratio > b.ratio; 10 } 11 12 double partBag(vector<double>& weight, vector<double>& value, double load) 13 { 14 int n = weight.size(); 15 vector<item> vec(n); 16 for (int i = 0; i < n; ++i) 17 { 18 vec[i].weight = weight[i]; 19 vec[i].value = value[i]; 20 vec[i].ratio = value[i] / weight[i]; 21 } 22 sort(vec.begin(), vec.end(), cmp); 23 double curLoad = 0.0, maxiValue = 0.0, remainLoad = load; 24 for (int i = 0; i < n; ++i) 25 { 26 if (remainLoad >= vec[i].weight)//全部装入 27 { 28 remainLoad -= vec[i].weight; 29 maxiValue += vec[i].value; 30 } 31 else//部分装入 32 { 33 maxiValue += remainLoad*vec[i].ratio; 34 break; 35 } 36 } 37 return maxiValue; 38 }
测试例:
1 int main() 2 { 3 vector<double> weight{ 4,2,9,5,5,8,5,4,5,5 }, value{ 3,8,18,6,8,20,5,6,7,15 }; 4 double load = 30.0; 5 double maxiValue = partBag(weight, value, load); 6 cout << "部分背包问题所能装的最大价值为:" << maxiValue << endl; 7 8 return 0; 9 }
例子3——会议安排问题
问题描述:如何在有限的时间内召开尽可能多的会议?
贪心策略:因为最早开始时间+持续时间最短=最早结束时间,所以每次选当前来看结束时间最早且不会与已有会议相冲突的会议
算法设计:1.定义一个结构体,包含会议的开始时间、结束时间
2.对会议按结束时间排序
3.按照贪心策略一步一步地选择会议,每安排上一个会议,便用变量last记录这个会议结束时间,下一次选择的会议的开始时间必须大于等于last