贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
当一个问题具有最优子结构性质时可以采用动态规划进行求解,但有时使用贪心策略会有更加简洁有效的算法。例如在找零钱(付钱)问题中,假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来支付K元,要求至少要用多少张纸币,使用贪心法的算法为:设k=K,首先选出一张面值不超过k元的最大纸币(设面值为m1),从k元中减去m1得到新的k,再选出一张面值不超过k元的最大纸币(设面值为m2)...直到凑够K元为止。可以看出,贪心算法总是在做当前看来最好的选择,即贪心算法不从整体最优上加以考虑,所做的选择只是局部上的最优选择。
比起使用动态规划,找零钱(付钱)问题用贪心算法求解更加简单,更直接且效率更高。但是贪心算法并不是对所有问题都能得出整体最优解,例如在上述问题中将纸币面值改为一元。五元和11元,答案是1张11元和4张一元,但是显然3个一元是更好的解法。虽然贪心算法并不是对所有问题都能得出整体最优解,但是对于相当多的问题仍能产生整体最优解,在一些情况下虽然不能给出整体最优解,但是最终结果仍是整体最优解的近似解。比起动态规划,贪心算法在某些情况下还是很具有性价比的。
贪心算法的基本要素
1、贪心选择性质
一个问题的整体最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。这就是贪心选择性质。对于一个具体问题,要确定它是否具有贪心选择性质,必须对每一步所作的贪心选择进行证明。
2、最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心法求解的关键。在实际应用中,至于什么问题具有什么样的贪心选择性需要具体分析
。
贪心算法的步骤:
②把求解的问题分成若干个子问题 。
③对每个子问题求解,得到子问题的局部最优解 。
④把子问题的解局部最优解合成原来解问题的一个解 。
例题:会场安排问题
描述:假设在足够多的会场里安排一批活动,并希望尽可能少的使用会场。设计一个有效的贪心策略进行安排。
输入:由文件input.txt给出第一行有一个整数k表示有k个待安排的活动。接下来k行中每行给出两个整数,分别表示活动的开始时间和结束时间。时间以0开始的分钟记。
输出:将计算的最小会场数输出到output.txt
输入示例: 输出示例:
input.txt output.txt
5 3
1 23
12 28
25 35
27 80
36 50
思路:对活动进行排序,开始时间越早排在越前面,如果两个活动时间相同,则结束时间越早的排在越前面,开始时间最早和持续时间最短的优先安排会场,并记录会场号,其余活动的开始时间大于或等于已安排活动的结束时间的安排在同一会场,若某活动的开始时间小于已经安排了会场的活动的结束时间,则安排在另一会场,记录会场号,依次循环,直到所有活动均被安排
#include<iostream>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<fstream>
using namespace std;
class acts_arrangement{
private:
ifstream in;
ofstream out;
int k,count;
int *start,*end;//分别保存开始和结束时间的数组指针
int read()
{
in>>k;
if(k<1)
return 0;
start=new int[k];
end=new int[k];
int i;
int s,e;
for(i=0;i<k;i++)//读入数据
{
in>>s>>e;
if(s<0||e<=s)
return 0;
start[i]=s;
end[i]=e;
}
sort(start,start+k);//从小到大排序
sort(end,end+k);//从小到大排序
return 1;
}
void arrange()
{
int j=0,i;
for(i=0;i<k;i++)
{
if(start[i]<end[j])//若最小的开始时间比比最小的结束时间小,需要加会场
count++;
else
j++;
}
}
void write()
{
out<<count;
}
public:
acts_arrangement(char *c1,char *c2)
{
in.open(c1);
out.open(c2);
if(read()==0)
exit(0);
count=0;
arrange();
write();
}
~acts_arrangement()
{
in.close();
out.close();
delete []start;
delete []end;
}
};
int main()
{
char c1[9],c2[10];
strcpy(c1,"input.txt");
strcpy(c2,"output.txt");
//cout<<"运行delete_num dn(c1,c2)" <<endl;
acts_arrangement aa(c1,c2);
return 0;
}