背包问题(贪心算法)
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
贪心算法还是比较好理解的一个算法,以前我也是这样认为的,感觉贪心就是每一步都做到最优解就可以了,但是后来结合问题发现自己的理解存在着一些问题。贪心算法比较经典的题目之一就是单源最短路径问题,这个问题在一些步骤上面我想了很久,有些细节想不通。这个问题以后有机会再讲。本次讲一讲背包问题。
背包问题就是有若干物品,每个物品有自己的价值和重量。背包有总重量。问题就是怎样将背包装的最大价值。背包问题也分很多种,贪心算法解决的是物品可以拆分的背包问题(就是物品可以分成几份装入)。这个问题用贪心还是比较好解决的。贪心选择是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。此问题就是将每次的放入看成每一步,要想解决问题,就是将每一步都放入最优解。也就是说,每一次的放入都要放入最佳的选择。讲到这里,就要说一说最佳的选择,每一次的放入的最佳的选择就是每次放入的物品都是剩余的物品中价值最大且质量最小的,这里就要引入一个物品的属性,物品的权重值。物品的权重值就是指物品的价值除以物品的质量。所以,本问题的每一次的最佳选择就是每次都选出权重值最大的物品。
问题的大致思路说完了,下面就讲一讲具体的算法。算法最开始是先声明物品类,因为后面要用到很多的物品属性,如果使用数组会有点麻烦,物品的属性有背包ID,物品价值,物品质量,物品权重值。在声明的时候,只要输入物品的前三个属性就可以了,物品的权重值可以由前三个推导出来。算法的接下来就是将物品的数组按物品的权重值排序,权重值大的排在数组的前面,方便后面的运算。算法的主体就是从数组中取出物品对象,计算比较物品的质量和当前背包剩余重量的大小,如果大于,就计算要放入的百分比。如果小于,就进行下一步的最佳选择。算法的大致思路及时这样。下面粘贴代码:
1 package sf; 2 3 import java.util.Scanner; 4 5 public class demo6 6 { 7 //选择排序将数组中的bag按权重排序 8 public static void sort(Bag[] p) 9 { 10 Bag t; 11 for(int i=0;i<p.length;i++) 12 { 13 int max=i; 14 t=p[i]; 15 for(int j=i;j<p.length;j++) 16 { 17 if(t.wi<p[j].wi) 18 { 19 t=p[j]; 20 max=j; 21 } 22 } 23 t=p[i]; 24 p[i]=p[max]; 25 p[max]=t; 26 27 } 28 } 29 //背包问题(贪心算法) 30 public static void bq(Bag[] p,int k,int w,double v) 31 { 32 if(p[k].weight<w) 33 { 34 v=v+p[k].value; 35 System.out.println(p[k].pid+"全部装入,当前背包价值为"+v); 36 w=w-p[k].weight; 37 bq(p, k+1, w, v); 38 }else{ 39 double a=w*p[k].wi;//当前价值 40 v=v+a; 41 System.out.println(p[k].pid+"装入了"+((double)w/p[k].weight)+",当前背包价值为"+v); 42 } 43 44 } 45 public static void main(String args[]) 46 { 47 System.out.println("请输入背包的容量w和物品的个数n"); 48 Scanner reader = new Scanner(System.in); 49 int w=reader.nextInt();//背包的容量 50 int n=reader.nextInt();//物品的个数 51 Bag[] p=new Bag[n]; 52 //10 10 a 10 10 b 10 15 c 53 System.out.println("请依次输入各个物品的重量w和价值v和名称s"); 54 int weigth; 55 int value; 56 String pid; 57 for(int i=0;i<n;i++) 58 { 59 weigth=reader.nextInt(); 60 value=reader.nextInt(); 61 pid=reader.next(); 62 p[i]=new Bag(weigth,value,pid); 63 } 64 //System.out.println(z[1]+""+v[1]); 65 sort(p); 66 bq(p,0,w,0.0); 67 for(int i=0;i<n;i++) 68 { 69 System.out.println(p[i].wi+" "+p[i].pid); 70 } 71 72 } 73 74 } 75 76 class Bag 77 { 78 public int weight;//重量 79 public int value;//价值 80 public double wi;//权重 81 public String pid;//背包名称 82 public Bag(int w,int v,String pid) 83 { 84 this.weight=w; 85 this.value=v; 86 this.pid=pid; 87 this.wi=(double)value/weight; 88 } 89 }
具体实现的问题大部分都在注释中了,虽然也没有多少注释,还是讲讲吧。在控制台输入的时候,要注意物品是先声明,在使用对象属性。最开始一直报错,想了很久,都是用数组用习惯了,直接就使用数组元素了。排序算法用的是选择,因为选择排序对于元素较少的情况下计算效果还是比较理想的。贪心算法的主体用的是递归。算法的实现还是比较简单的。主要是理解贪心算法的思想,做出每一步都是最优解的选择。