一、概念
贪心法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。它不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。
二、问题分析
1.背包问题
将物品按单位重量所具有的价值排序。总是优先选择单位重量下价值最大的物品单位重量所具有的价值:Vi / Wi。按照我们的贪心策略,单位重量的价值排序: 物品1 > 物品2 > 物品3。此,我们尽可能地多拿物品1,直到将物品1拿完之后,才去拿物品2。
2.找零钱问题
从大面额开始找零,依次往下找,直到刚好找零停止。
三、问题解决
背包问题的伪代码:
1.初始化数组x长度为n,初始化数组w[]存放各个重量,初始化数组v[]存放各个物品的总价值,初始化C作为背包的容量。
2.初始化maxValue作为最大价值,赋值为零
3.构建for循环,循环变量i=0,从0到n执行下列操作
3.1x[i]=0//将数组x里面初值赋为零
3.2i++;
4.初始化j;
5.构建for循环,循环变量j=0,从w[j]到C执行下列操作//C是背包容量,当物品容量小于或者等于背包容量时才能装入背包,否则需要切割。
5.1将j的值赋给x[j];
5.2maxValue+=v[j];
5.3C=C-w[j];
5.4j++;
6.x[j]=C/w[j];
7.maxValue+=x[j]*v[j];
8.返回maxValue的值。
找零钱问题的伪代码:
1.初始化数组coin存放找的零钱的面额,初始化m作为数组coin的长度,初始化n作为要找的零钱大小,定义初始化数组num[]作为要找零钱面额的各个张数。
2.把n的值赋给定义的balance;
3.构建for循环,循环变量i从0到m执行下列操作
3.1如果balance>=coin[i]执行下一步操作
3.1.1 num[i]=balance/coin[i];
3.1.2balance-=num[i]*coin[i];
3.2如果balance==0执行下一步操作
3.2.1break;
4.定义初始化count为零作为找零的张数;
5.如果balance不等于零执行下一步操作
5.1return 0
6.构建for循环,循环变量i从零到m执行下列操作
6.1count+=num[i];
7.return count;
四、代码实现
1.背包问题
public class Knapsack {
double KnapSack(int w[],int v[],int n,int C){
int[] x=new int[n];
int maxValue=0;
for(int i=0;i<n;i++)
{
x[i]=0;
}
int j;
for(j=0;w[j]<C;j++)
{
x[j]=j;
maxValue+=v[j];
C=C-w[j];
}
x[j]=C/w[j];
maxValue+=x[j]*v[j];
return maxValue;
}
public static void main(String[] args) {
Knapsack j=new Knapsack();
int[] w={20,30,10};
int[] v= {60,120,50};
int C=50,n=3;
double value=j.KnapSack(w,v,n,C);
System.out.println(value);
}
}
2. 找零钱问题
public class getmincoinbygd {
public int getmincoinBy(int[] coin,int m,int n)
{
int balance=n;
int[] num=new int[m];
for(int i=0;i<m;i++)
{
if(balance>=coin[i])
{
num[i]=balance/coin[i];
balance-=num[i]*coin[i];
}
if(balance==0)
{
break;
}
}
int count=0;
if(balance!=0)
{
return 0;
}
for(int i=0;i<m;i++)
{
count+=num[i];
}
return count;
}
public static void main(String[] args)
{
getmincoinbygd j=new getmincoinbygd();
int [] coin={100,50,20,10,5,1};
int result=j.getmincoinBy(coin,6,110);
System.out.println(result);
}
}
五、总结
在求解过程中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
具体步骤为:
1.建立数学模型来描述问题;
2.求解的问题分成若干个子问题;
3.每一子问题求解,得到子问题的局部最优解;
4.子问题的解局部最优解合成原来解问题的一个解。
贪心法、动态规划法、分治法有什么区别呢?
动态规划法与分治法和贪心法类似,它们都是将问题实例归纳为更小的、相似的子问题,并通过求解子问题产生一个全局最优解。
不同点:
其中贪心法的当前选择可能要依赖已经作出的所有选择,但不依赖于有待于做出的选择和子问题。因此贪心法自顶向下,一步一步地作出贪心选择;而分治法中的各个子问题是独立的 (即不包含公共的子子问题),因此一旦递归地求出各子问题的解后,便可自下而上地将子问题的解合并成问题的解。但不足的是,如果当前选择可能要依赖子问题的解时,则难以通过局部的贪心策略达到全局最优解;如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题。解决上述问题的办法是利用动态规划。该方法主要应用于最优化问题,这类问题会有多种可能的解,每个解都有一个值,而动态规划找出其中最优(最大或最小)值的解。若存在若干个取最优值的解的话,它只取其中的一个。在求解过程中,该方法也是通过求解局部子问题的解达到全局最优解,但与分治法和贪心法不同的是,动态规划允许这些子问题不独立,也允许其通过自身子问题的解作出选择,该方法对每一个子问题只解一次,并将结果保存起来,避免每次碰到时都要重复计算。因此,动态规划法所针对的问题有一个显著的特征,即它所对应的子问题树中的子问题呈现大量的重复。动态规划法的关键就在于,对于重复出现的子问题,只在第一次遇到时加以求解,并把答案保存起来,让以后再遇到时直接引用,不必重新求解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。