01背包问题优化与java代码的详解(动态规划)

来源:互联网 发布:手机相片日期软件 编辑:程序博客网 时间:2024/05/20 12:21
由于算法讲解文字较多,直接摘图,撰写本文的目的在于写出我对代码的详细解读。如有错误,请大家指出。注:前面问题描述以及算法分析摘取其他优秀网站,后面附有详细的代码讲解,以及图片讲解

1
2
3
4
5
6
7
8

代码运行分析如下:

9

10

11

12

13

14

★代码实现(java):

//0-1背包问题(跳跃点)/*p用于保存所有可能的最优值 * 设题目为w={2,2,6,5,4};v={6,3,5,4,6};c=10;时 * p保存的数据如下: * p={ *    (0,0),                                    <<=p[6] , head[6]=0,记录(0,0)所在的行的位置 *    (0,0),(4,6),                              <<=p[5] , head[5]=1,记录(0,0)所在的行的位置 *    (0,0),(4,6),(9,10),                       <<=p[4] , head[4]=3,记录(0,0)所在的行的位置 *    (0,0),(4,6),(9,10),(10,11),               <<=p[3] , head[3]=6,记录(0,0)所在的行的位置 *    (0,0),(2,3),(4,6),(6,9),(9,10),(10,11),   <<=p[2] , head[2]=10,记录(0,0)所在的行的位置 *    (0,0),(2,6),(4,9),(6,12),(8,15)           <<=p[1] , head[1]=16,head[0]=21,head[1]记录(0,0)所在的行的位置,head[0]记录(8,15)接下来下一个值的行的位置 *    } * 假设每个物品都可以加入背包后,且此时有最优值, * 共有n个物品,则设存在第n+1个物品,且重量为0,价值为0; * 则p[i]的元素的个数一次自增1,则p的行数为1+2+3+4+..+n+(n+1)=(1+(n+1))*(n+1)/2 * */public class knapsack_problem {       static double[][] p;       static int[] x;       public static void main(String[] args){              double[] w={2,2,6,5,4};      //可自行修改代码使w,v,c为用户进行输入              double[] v={6,3,5,4,6};              double c=10;              p=new double[((1+(w.length+1))*(w.length+1))/2][2];     //p用于保存所有可能的最优值,行数为((1+项数加1)*项数加1)/2,列数为2,(项数加1,因为假设除了存在w.length个物品外,还存在一个第w.length号物品,且重量为0,价值为0))              x=new int[w.length+1];             //在求得最优值时,x[i]记录第i个物品是否放进背包,0为不放进,1为放进去              int[] head=new int[w.length+2];     //head记录每个p[i]方式的记录的第一个跳跃点,具体看代码前面注释              knapsack_problem pack =new knapsack_problem();              System.out.println("具体实例为w={2,2,6,5,4};v={6,3,5,4,6};c=10时,有:");              System.out.println("求得的最优值为:"+pack.knapsack(w,v,c,p,head));  //求解最优值              pack.traceback(w,v,c,p,head,x);                                     //求得最优值后,求取放进背包的物品              pack.show_traceback();                                              //求得最优值后,显示放进背包的物品       }       //求得最优值后,显示被放进背包的物品       private void show_traceback() {              System.out.print("装入背包分别为第");              for(int i=0;i<x.length;i++){                     if(x[i]==1) System.out.print(i+" ");              }              System.out.println("个物品");       }    /*注意:以下注释的p[i]均指代码前注解所指的p[i],而不是指数组p的第i行*/    //p[i][0]保存当前最优值的质量(即可能占用的背包空间),p[i][1]保存当前最优值的价值       public double knapsack(double[] w,double[] v,double c,double[][] p,int[] head){              int n=v.length-1;              head[n+2] = 0; //指向p[6]的第一个元素(0,0)在p中的位置              p[0][0] = 0;  //默认存在第六个物品,质量为0,价值为0,放进背包              p[0][1] =0;              int left = 0,right = 0,   //left和right分别指向p[i]的第一个元素和最后一个元素                     next = 1;             //next指向p中接下来还没存数据的空行              head[n+1] =1;             //指向p[5]的第一个元素(0,0)在p中的位置              for(int i=n;i>=0;i--){    //从倒数第一个物品开始,对物品逐个进行判断是否放进背包,注:w[0]为第一个物品的重量                     int k=left;                     for(int j=left;j<=right;j++){                            if(p[j][0]+w[i]>c) break;    //当第i个物品加进背包超出10(背包承受最大重量),跳出循环,不放进去                            double y=p[j][0]+w[i],       //当第i个物品加进背包时,产生重量保存在y中                                      m=p[j][1]+v[i];       //当第i个物品加进背包时,产生价值保存在m中                            while(k<=right && p[k][0]<y){//当不将第i个物品加进背包的重量小于加入背包的重量时,直接添加p[k][0]和p[k][1]到next指向的行上                                   p[next][0] = p[k][0];                                   p[next++][1] = p[k++][1];//赋值后,k,next先后分别进行自增                            }                            if(k<=right && p[k][0]==y){  //当不将第i个物品加进背包与加入背包的重量相同时                                   if(m<p[k][1]){           //若不将第i个物品加进背包产生的价值更大,                                          m = p[k][1];         //则把第i个物品没有加进背包时产生的价值赋值给m                                   }                                   k++;                            }                            if(m>p[next-1][1]){    //当将第i个物品加进背包产生的价值更大,将其加入背包                                   p[next][0]=y;                                   p[next++][1]=m;                            }                            while(k<=right && p[k][1]<=p[next-1][1]){k++;}      //当不将第i个物品加进背包产生的价值更大,k++                     }                     while(k<=right){           //将余下的值(k到right,即没有进行比较的数据),直接追加到p上                            p[next][0] = p[k][0];                            p[next++][1] = p[k++][1];                     }                     left = right+1;   //指向当前p[i]的第一个元素(0,0)的位置                     right = next-1;   //指向当前p[i]的最后一个元素的位置                     head[i] = next;   //记录下一组p[i]的第一个元素(0,0)的位置              }                  return p[next-1][1];        //返回最优值       }       //分别对每个物品是否在背包中       public void traceback(double[] w,double[] v,double c,double[][] p,int[] head,int[] x){              int n = w.length-1;              double j = p[head[0]-1][0],   //j为最优值的重量                        m = p[head[0]-1][1];   //m为最优值的价值              for(int i=1;i<=n+1;i++){                     x[i] = 0;            //设定第i个物品不在背包中,即x[i] = 0                     for(int k = head[i+1];k<=head[i]-1;k++){  //head[i+1]head[i]-1依次分别为p[2],p[3],p[4],p[5],p[6]的第一个元素和最后一个元素的位置                            if(p[k][0]+w[i-1]==j && p[k][1]+v[i-1]==m){  //eg:(6,9)+(2,6)=(8,15),即第一个物品在背包中,(4,6)+(2,3)=(6,9),即第二个物品在背包中,(0,0)+(4,6)=(4,6),即第五个物品在背包中                                   x[i]=1;      //第i个物品位于背包中,可产生最优值,即x[i] = 1                                   j = p[k][0]; //j变化为:j=8=>>j=6=>>j=4                                   m = p[k][1]; //m变化为:m=15=>>m=9=>>m=6                                   break;      //跳出循环,从而对下一个物品进行判断是否在背包中                            }                     }              }       }}

运行结果如下:

结果

原创粉丝点击