贪心算法
求解问题时,总是做出在当前看来是最好的选择,得到的结果是某种意义下的局部最优解
贪心选择
所求问题的整体最优解可以通过一系列局部最优解,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。采用自顶向下,以迭代的方法相继做出选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择的性质,我们必须证明每一步所作的贪心选择最终能得到问题的最优解。通常可以首先证明问题的一个整体最优解,是从贪心选择开始的,而且作了贪心选择后,原问题简化为一个规模更小的类似子问题。然后,用数学归纳法证明,通过每一步贪心选择,最终可得到问题的一个整体最优解。
最优子结构
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性值。运用贪心策略在每一次转化时都取得了最优解,问题的最优子结构性质是该问题可用贪心算法或动态规划算法求解的关键特征。贪心算法的每一次操作都对结果产生直接影响,而动态规划则不是。贪心算法对每个子问题的解决方案都做出选择,不能回退;动态规划则会根据以前的选择结果对当前进行选择,有回退功能。动态规划主要运用于二维或三维问题,而贪心一般是一维问题。
基本思路
从问题的某一个初始解除法一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止。
过程
1、 建立数学模型来描述问题;
2、 把求解的问题分成若干个子问题;
3、 对每一子问题求解,得到子问题的局部最优解;
4、 把子问题的解局部最优解合成原来解问题的一个解。
常见贪心解决问题
1、数字组合问题
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
**思路:**容易想到贪心法,但是有个易错点,很多人把整数按从小到大的顺序连接起来,显然是错的 。
但其实这个题目确实可以用贪心算法解决,只是刚才的贪心标准不对,正确的贪心标准是:先把整数化成字符串,可以用
toString()方法,再比较字符串相加结果 a+b 和 b+a ;如果 a+b >b+a ,就把 b 排在 a 的前面,反之 a 排在 b 的前面,
上面 的思想也能用于排成最大的数字。
代码如下
function PrintMinNumber(numbers)
{
if(numbers.length==0){
return "";
}
for(var i=0;i<numbers.length;i++){
for(var j=i+1;j<numbers.length;j++){
if(numbers[i].toString()+numbers[j].toString()>numbers[j].toString()+numbers[i].toString()){
var temp=numbers[i];
numbers[i]=numbers[j];
numbers[j]=temp;
}
}
}
var str="";
for(var i=0;i<numbers.length;i++){
str+=numbers[i];
}
//转化成数字返回
return str-0;
};
2、活动安排问题
设有n个活动的集合E={1,2,……,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si<fi。如果选择了活动i,则它在时间区间[si,fi]内占用资源。若区间[si,fi]与区间[sj,fj]不相交,则称活动i与活动j是相容的。也就是说,当si>=fj或者sj>=fi时,活动i与活动j相容。活动安排问题就是要在所给的活动集合中选出最大的相容活动子集合。
思路: 将活动按照结束时间从小到大排序,用 i 表示第 i 个活动,s[i] 表示该活动的开始时间, e[i] 表示该活动的结束时间。按照从小到大排序,选择出结束时间尽可能早的活动,并且满足后一个活动的起始时间晚于前一个活动的结束时间,全部找出这些活动就是最大的相容活动子集合。显然第一个活动肯定是会被选择的。遍历活动,将上述条件作为限制,添加活动。
3、道路规划和最小生成树
prim 算法 和 Kruscal 算法求最小生成树