顾名思义,贪心算法总是作出在当前看来最好的选择,也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。当然,希望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。如单元最短路径问题,最优生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
贪心算法的基本思路:
- 建立数学模型来描述问题;
- 把求解的问题分成若干个子问题;
- 对每个子问题求解,得到子问题的局部最优解;
- 把子问题的局部最优解合成原来问题的一个解;
贪心算法的一般框架
Greedy(C) //C是问题的输入集合即候选集合
{
S = {};//初始解集合为空集
while(not solution(S)) //集合S没有构成问题的一个解
{
x=select(C); //在候选集合C中做贪心选择
if feasible(S, x)//判断集合S中加入x后的解是否可行
S= S+{x};
C = C-{x};
}
}
贪心算法的适用问题
对于一个具体的问题,是否能够通过贪心算法得到整体最优解呢?这个问题很难给予肯定的回答。但是能用贪心算法求解的问题一般具有两个重要的性质:贪心选择性质和最优子结构性质。
贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择达到;当一个问题的最优解包含子问题的最优解时,称此问题具有最优子结构性质。
贪心算法的经典示例
示例一:0-1背包问题
问题描述:背包的容量为C,物品i的重量为wi,物品i的价值为vi,可以将物品部分装入背包(之前的背包问题不能部分装入,只能装或者不装),求如何装入物品可以是总价值最大。
//这个方法是求最大的价值
public double maxValue(int C, int[] w, int[] v, int n) {
if( C <= 0 || n <= 0)
return 0;
float[] aver = new float[n];
int[] index = new int[n];
double sum = 0;
for(int i = 0; i < n; i++) {
aver[i] = (float) (v[i] + 0.0)/w[i];
index[i] = i;
}
mergeSort(aver, index, 0, n-1);
for(int i = n-1; i >= 0; i--) {
if(w[index[i]] < C) {
sum += v[index[i]];
C -= w[index[i]];
}else {
sum += C*aver[index[i]];
break;
}
}
return sum;
}
//对物品的单位价值进行排序
public void mergeSort(float[] aver, int[] index, int start, int end) {
if(start >= end)
return;
int mid = (start + end) >> 1;
mergeSort(aver, index, start, mid);
mergeSort(aver, index, mid + 1, end);
merge(aver, index, start, mid, end);
}
public void merge(float[] aver, int[] index, int start, int mid, int end) {
int i = start;
int j = mid + 1;
int k = 0;
float[] temp1 = new float[end - start + 1];
int[] temp2 = new int[end - start + 1];
while(i <= mid && j <= end) {
if(aver[i] <= aver[j]) {
temp1[k] = aver[i];
temp2[k] = index[i];
k++;
i++;
}else {
temp1[k] = aver[j];
temp2[k] = index[j];
k++;
j++;
}
}
if(i <= mid) {
while(i <= mid) {
temp1[k] = aver[i];
temp2[k] = index[i];
k++;
i++;
}
}
if(j <= end) {
while(j <= end) {
temp1[k] = aver[j];
temp2[k] = index[j];
k++;
j++;
}
}
for(int m = 0; m <= end-start; m++) {
aver[start + m] = temp1[m];
index[start + m] = temp2[m];
}
}
示例二:最大多位整数
问题描述:有n个正整数,将其按照一定的顺序排列,输出最大的多位整数。
其实就是需要找到一个排序规则。首先将每个整数转为字符串的形式,对于其中的任意的两个字符串a和b,如果 a + b构成的字符串大于 b+ a构成的字符串,则a排在b之前,否则,b排在a之前。
public void maxInteger(int[] a) {
String[] aa = new String[a.length];
for(int i = 0; i < a.length; i++)
aa[i] = String.valueOf(a[i]);
Arrays.sort(aa, new Comparator<String>(){
public int compare(String o1, String o2) {
String s1 = o1 + o2;
String s2 = o2 + o1;
return s2.compareTo(s1);
}
});
StringBuilder sb = new StringBuilder();
for(String s: aa)
sb.append(s);
System.out.println(sb.toString());
}
总结:平常贪心算法用的不多,没怎么见过类似的题,但是在一些场合,贪心算法真的可以极大简化程序的设计,是否能用贪心,只能举些例子尝试下了,多积累些贪心算法解决问题的题。。