贪心算法如何贪心

Posted 每天学Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心算法如何贪心相关的知识,希望对你有一定的参考价值。

在前面学习最短路径和最小生成树的时候,我们发现Dijkstra算法,Prim算法,Kruskal算法都是属于典型的贪心算法应用。这篇文章就是对于贪心算法的入门介绍

贪心算法

贪心算法(又称贪婪算法)是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。

贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

简单的说就是我们在问题处理中,将问题分解为很多步,然后在每一步的求解过程中,“贪婪”的选择最佳操作,并希望通过一系列的最优选择, 能够产生一个问题的(全局的)最优解

算法思路

贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,不能回退,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中, 直到把所有数据枚举完,或者不能再添加算法停止。

最优子结构

当一个问题的最优解包含着它的子问题的最优解时,称此问题具有最优子结构性质。问题所具有的这个性质是该问题可用动态 规划算法或贪心算法求解的一个关键特征。

我们通过下面两个例题来看下什么时候选用贪心算法求解。

例题一

题目:给出0-9的数字任意个,比如数字0两个,数字1两个,数字5三个。让你用所给的数据组成一个最小值,0不能放在首位。

解题策略分析:

对于组装数据的策略其实有很多种,但是如果组装成一个最小值,那么其实我们很容易想到策略其实只有一种, 即每次都选取规定中的最小的值去组装新的数。

既然策略只有一种,且是用给定的数去组成一个新的最小的数,那么运用贪心算法之前,我们需要考虑每一步组装新值所选取的数据如果是符合规定的最小值, 那么能不能保证最终的数值是最小的。

这里显而易见,每一步的首位数字是符合规定的最小值,那么最终组成的数据也是最小的(可以使用数学归纳法证明)。所以使用贪心算法是可以的。

例题二

题目:假如现在仓库囤有一批同款产品不同规格的货物,货A是30吨,货B是50吨,货C是30吨,总价值分别是,30万,50万,30万,货物A,B,C不可分解。现在有人 准备收购60吨。那么如何分配呢?

解题策略分析:

这道题和例题一的区别在于,例题一策略只有一种,就是每次选取最小的即可,但是这道题我们无法准确定义策略有集中,因为他可能的策略都有可能是最优策略:

策略一:卖货物价值最高的B,50万,但是如果卖A和B会有60万,所以不行

策略二:卖货物最轻的A,C,一共60万。看似可行,但是我们需要想到的是,这些数据可能是任意的, 如果A是10吨,那么A,C只有40万。

策略三:卖单价最贵的,我们会发现他们单价是一样的,没法选。如果我们在该策略下再制定第二策略,优先卖重量小的, 那么其实就会出现上面的策略二问题。

走到这里我们就发现,贪心算法不能完全处理某一策略下的所有数据。因为每一步的最优解,会影响后续选择(由于60吨的限制)。

我们想一下为什么不能使用贪心算法,那是因为我们无法确定贪心策略,限制贪心策略的原因是ABC不能拆分, 不能拆分,就有了上面三种可能,但是如果可以拆分,那么策略三是可行的,先卖单价最贵的,但是如果单价相同那么得到的结果不是唯一的。

如何选用

贪心算法并不能总求得问题的整体最优解。但对于某些问题,却总能求得整体最优解,这要看问题是什么了。只要能满足贪心算法的两个性质:

贪心选择性质和最优子结构性质,贪心算法就可以出色地求出问题的整体最优解。即使某些问题,贪心算法不能求得整体的最优解,贪心算法

也能求出大概的整体最优解。如果你的要求不是太高,贪心算法是一个很好的选择。最优子结构性质是比较容易看出来的,但是贪心选择性质

就没那么容易了,这个时候需要证明。证明往往使用数学归纳法。

存在的问题

不能保证求得的最后解是最佳的

不能用来求最大值或最小值的问题

只能求满足某些约束条件的可行解的范围

例题一代码(C++)

//
// main.cpp
// Minimun
//
// Created by 陈龙
// Copyright © 2019 陈龙. All rights reserved.
//

#include <iostream>
using namespace std;

int main(int argc, const char * argv[]) {
int t[10];
for (int i=0; i<10; i++) {
cout<<"输入"<<i<<"的个数:";
cin>>t[i];
}
cout<<"结果"<<endl;
//组装最小值
for(int j=1;j<9;j++){//首位
if (t[j]>0) {
cout<<t[j]--;
break;
}
}
for(int i = 0 ;i<10;i++){//
for(int j=0;j<t[i];j++){
cout<<i;
}
}

return 0;
}


以上是关于贪心算法如何贪心的主要内容,如果未能解决你的问题,请参考以下文章

贪心算法----区间覆盖问题(POJ2376)

Contig|scaffold|N50|L50|NG50|贪心算法|de bruiji graph|

贪心算法学习手册开放下载!!

贪心算法如何贪心

算法| 贪心算法:如何用贪心算法实现Huffman压缩编码?

贪心算法(各种贪心题目)