Java数据结构动态规划

来源:互联网 发布:知乎周刊plus 日语 编辑:程序博客网 时间:2024/06/07 23:00

一、基本概念

    动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。

二、基本思想与策略

    基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

    由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。

    与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)

 


三、适用的情况

能采用动态规划求解的问题的一般要具有3个性质:

    (1) 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

    (2) 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。

   (3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

 


四、求解的基本步骤

     动态规划所处理的问题是一个多阶段决策问题,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条活动路线(通常是求最优的活动路线)。如图所示。动态规划的设计都有着一定的模式,一般要经历以下几个步骤。

    初始状态→│决策1│→│决策2│→…→│决策n│→结束状态

                      图1 动态规划决策过程示意图

    (1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。

    (2)确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。

    (3)确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程

    (4)寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。

    一般,只要解决问题的阶段状态状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。

实际应用中可以按以下几个简化的步骤进行设计:

    (1)分析最优解的性质,并刻画其结构特征。

    (2)递归的定义最优解。

    (3)以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值

    (4)根据计算最优值时得到的信息,构造问题的最优解

 


五、算法实现的说明

    动态规划的主要难点在于理论上的设计,也就是上面4个步骤的确定,一旦设计完成,实现部分就会非常简单。

     使用动态规划求解问题,最重要的就是确定动态规划三要素

    (1)问题的阶段 (2)每个阶段的状态

    (3)从前一个阶段转化到后一个阶段之间的递推关系

     递推关系必须是从次小的问题开始到较大的问题之间的转化,从这个角度来说,动态规划往往可以用递归程序来实现,不过因为递推可以充分利用前面保存的子问题的解来减少重复计算,所以对于大规模问题来说,有递归不可比拟的优势,这也是动态规划算法的核心之处

    确定了动态规划的这三要素,整个求解过程就可以用一个最优决策表来描述最优决策表是一个二维表,其中行表示决策的阶段,列表示问题状态,表格需要填写的数据一般对应此问题的在某个阶段某个状态下的最优值(如最短路径,最长公共子序列,最大价值等),填表的过程就是根据递推关系,从1行1列开始,以行或者列优先的顺序,依次填写表格,最后根据整个表格的数据通过简单的取舍或者运算求得问题的最优解。

          f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}


装配线调度问题


问题描述 

[html] view plain copy
  1. 某汽车工厂有2个装配线,每个装配线有n个装配站(按顺序编号1~n),两个装配线对应的装配站执行相同的功能,但所用的时间可能不同.经过第i条流水线(i=1,2)的第j个装配站所花的时间为Aij。从第i条流水线的第j个装配站移到第j+1个装配站的时间可以忽略,而移到另外一个流水线的下一个装配站则需要一定的时间Tij。  
  2. 汽车进入流水线不需要花时间,出流水线时需要花时间Tin。  
  3. 汽车的装配需要按顺序经过所有装配站。  
  4. 现在已知装配时间Aij和转移时间Tij,要求输出装配一辆汽车所需要的最短时间。  

动态规划的算法思想

动态规划与贪心策略类似,将一个问题的解决方案视为一系列决策的结果。不同的是,贪心算法每采用一次贪心选择便做出一个不可撤回的决策,而在动态规划中,还要考察每个最优决策序列中是否包含一个最优决策自序列。使用动态规划时,所求的问题应具有以下两种性质:
  • 最优子结构性质
        所求问题的最优子结构性质是采用动态规划算法的条件之一,这种性质又被称为最优化原理。动态规划方法采用最优化原理来建立用于计算最优解的递归式。所谓最优化原理即不管前面的策略如何,此后的决策必须是基于当前状态(由上一次决策产生)的最优决策。由于对于有些问题的某些递归式来说并不一定能保证最优原则,因此在求解问题时有必要对它进行验证。若不能保持最优原则,则不可应用动态规划方法。在得到最优解的递归式之后,需要执行回溯以构造最优解。当最优决策序列中包含最优决策子序列时,可建立动态规划递归方程,它可以帮助我们高效的解决问题
  • 子结构重迭性质
         人们总希望编写一个简单的递归程序来求解动态规划方程。然而,如果不努力的去避免重复计算,递归程序的复杂性将非常可观。如果在递归程序设计中解决了重复计算问题,复杂性将大幅度下降。这种方法的思想是:由程序设置“备忘录”,每计算出一个新的子结构的解时,都保存起来。当遇到一次递归时,判断是否已经计算,如果已经计算,只需取出先前保存的结果即可。动态规划递归方程也可以用迭代方式来求解,这时很自然的避免了重复计算。尽管迭代程序与避免重复计算的递归程序有相同的重复性,但迭代程序不需要附加的递归栈空间,因此将避免重复计算的递归程序更快

装配线调度代码(c语言版)

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.   
  5. #define LINE 2  
  6. #define N 6  
  7.   
  8. int main()  
  9. {  
  10.     // 装配线装配时间  
  11.     const int product[LINE][N] = {{7, 9, 3, 4, 8, 4}, {8, 5, 6, 4, 5, 7}};  
  12.   
  13.     // 装配线转移时间  
  14.     const int transport[LINE][N - 1] = {{2, 3, 1, 3, 4}, {2, 1, 2, 2, 1}};  
  15.   
  16.     // 进装配线的时间  
  17.     const int e1 = 2;  
  18.     const int e2 = 4;  
  19.   
  20.     // 出装配站的时间  
  21.     const int o1 = 3;  
  22.     const int o2 = 2;  
  23.   
  24.     // 保存从起点到终点s(i, j)的最短时间  
  25.     int final[LINE][N] = {{0}, {0}};      
  26.   
  27.     // 保存总时间  
  28.     int time = 0;  
  29.   
  30.     // 保存经过路线  
  31.     int line[LINE][N] = {{0}, {0}};  
  32.       
  33.     // 保存最后从哪条线走出装配线  
  34.     int line_out = -1;  
  35.   
  36.     // 保存正序的最短装配路线  
  37.     int path[N];  
  38.     memset(path, -1, sizeof(path));  
  39.   
  40.     int i, j;  
  41.   
  42.   
  43.     // 计算最快时间  
  44.     final[0][0] = e1 + product[0][0];  
  45.     final[1][0] = e2 + product[1][0];  
  46.   
  47.     for (j = 1; j < N; j ++) {  
  48.         if (final[0][j - 1] + product[0][j] <= final[1][j - 1] + transport[1][j - 1] + product[0][j]) {  
  49.             final[0][j] = final[0][j - 1] + product[0][j];  
  50.             line[0][j] = 0;  
  51.         }else {  
  52.             final[0][j] = final[1][j - 1] + transport[1][j - 1] + product[0][j];  
  53.             line[0][j] = 1;  
  54.         }  
  55.   
  56.         if (final[1][j - 1] + product[1][j] <= final[0][j - 1] + transport[0][j - 1] + product[1][j]) {  
  57.             final[1][j] = final[1][j - 1] + product[1][j];  
  58.             line[1][j] = 1;  
  59.         }else {  
  60.             final[1][j] = final[0][j - 1] + transport[0][j - 1] + product[1][j];  
  61.             line[1][j] = 0;  
  62.         }  
  63.     }  
  64.   
  65.     if (final[0][N - 1] + o1 <= final[1][N - 1] + o2) {  
  66.         line_out = 0;  
  67.         time = final[0][N - 1] + o1;  
  68.     }else {  
  69.         line_out = 1;  
  70.         time = final[1][N - 1] + o2;  
  71.     }  
  72.     printf("总共花费的时间是:%d\n", time);  
  73.   
  74.     // 站号递增的顺序输出各装配站  
  75.     for (j = N - 1, i = line_out; j >= 1; j --) {  
  76.         i = line[i][j];  
  77.         path[j] = i;  
  78.     }  
  79.   
  80.     for (i = 1; i < N; i ++) {  
  81.         printf("line: %d, station: %d\n", path[i] + 1, i);  
  82.     }  
  83.     printf("line: %d, station: %d\n", line_out + 1, N);  
  84.   
  85.     return 0;  
  86. }  


最长公共子序列(LCS)

题目描述


[html] view plain copy
  1. 如果Z是X的一个子序列又是Y的一个最长公共子序列。例如,如果X = {A, B, C, B, D, A, B}, Y = {B, D, C, A, B, A},则{B, C, B, A}是X和Y的一个LCS,序列 {B,D, A,B}也是,因为没有长度为5的或更大公共子序列  

思路


(1)梳理状态的变化情况,找到最优子结构:
设X = (x1, x2, x3, ..., xm), Y = (y1, y2, y3...yn)为两个序列,并设Z = (z1, z2, z3,...zk)为X和Y的任意一个LCS
  1. 如果xm == yn,那么zk == xm == yn,而且Z(k - 1)是X(m-1)和Y(n-1)的一个LCS;
  2. 如果xm != yn,那么zk != xm,蕴含Z是X(m-1)和Y的一个LCS;
  3. 如果xm != yn,那么zk != yn,蕴含Z是X和Y(n-1)的一个LCS;
(2)找到一个递归的解:
如果xm == yn,必须找到X(m - 1)和Y(n - 1)的一个LCS。将xm = yn添加到这个LCS上,可以产生X和Y的一个LCS;如果xm != yn,就必须解决两个子问题:找到x(m - 1)和y一个LCS,以及找出X和Y(n-1)的一个LCS,以及找出X和Y(n - 1)的一个LCS,这两个LCS中,较长的就是X和Y的一个LCS。由LCS的最优子结构可得到递归式:



最长公共子序列代码(c语言版)

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.   
  5. #define N 1002  
  6.   
  7. int num[N][N], b[N][N];  
  8.   
  9. int longestCommonString(char *str1, char *str2, int len1, int len2);  
  10.   
  11. int main()  
  12. {  
  13.     int l, len1, len2;  
  14.     char str1[N], str2[N];  
  15.   
  16.     while (scanf("%s %s", str1, str2) != EOF) {  
  17.         len1 = strlen(str1);  
  18.         len2 = strlen(str2);  
  19.   
  20.         l = longestCommonString(str1, str2, len1, len2);  
  21.         printf("lcs is %d\n", l);  
  22.     }  
  23.   
  24.     return 0;  
  25. }  
  26.   
  27. int longestCommonString(char *str1, char *str2, int len1, int len2)  
  28. {  
  29.     int i, j;  
  30.   
  31.     for (i = 0; i < len1; i ++) {  
  32.         num[i][0] = 0;  
  33.     }  
  34.     for (j = 0; j < len2; j ++) {  
  35.         num[0][i] = 0;  
  36.     }  
  37.   
  38.     for (i = 1; i <= len1; i ++) {  
  39.         for (j = 1; j <= len2; j ++) {  
  40.             if (str1[i - 1] == str2[j - 1]) {  
  41.                 num[i][j] = num[i - 1][j - 1] + 1;  
  42.                 b[i][j] = 0;  
  43.             }else {  
  44.                 if (num[i - 1][j] >= num[i][j - 1]) {  
  45.                     num[i][j] = num[i - 1][j];  
  46.                     b[i][j] = 1;  
  47.                 }else {  
  48.                     num[i][j] = num[i][j - 1];  
  49.                     b[i][j] = -1;  
  50.                 }  
  51.             }  
  52.         }  
  53.     }  
  54.   
  55.     return num[len1][len2];  

  1. }  


六、动态规划算法基本框架

 1 for(j=1; j<=m; j=j+1) // 第一个阶段 2    xn[j] = 初始值; 3  4  for(i=n-1; i>=1; i=i-1)// 其他n-1个阶段 5    for(j=1; j>=f(i); j=j+1)//f(i)与i有关的表达式 6      xi[j]=j=max(或min){g(xi-1[j1:j2]), ......, g(xi-1[jk:jk+1])}; 8  9 t = g(x1[j1:j2]); // 由子问题的最优解求解整个问题的最优解的方案10 11 print(x1[j1]);12 13 for(i=2; i<=n-1; i=i+115 {  17      t = t-xi-1[ji];18 19      for(j=1; j>=f(i); j=j+1)21         if(t=xi[ji])23              break;25 }
具体例子

用一个实际例子来体现动态规划的算法思想——硬币找零问题。

硬币找零问题描述:现存在一堆面值为 V1、V2、V3 … 个单位的硬币,问最少需要多少个硬币才能找出总值为 T 个单位的零钱?假设这一堆面值分别为 1、2、5、21、25 元,需要找出总值 T 为 63 元的零钱。

很明显,只要拿出 3 个 21 元的硬币就凑够了 63 元了。

基于上述动态规划的思想,我们可以从 1 元开始计算出最少需要几个硬币,然后再求 2 元、3元…每一次求得的结果都保存在一个数组中,以后需要用到时则直接取出即可。那么我们什么时候需要这些子问题的解呢?如何体现出由子问题的解得到较大问题的解呢?

其实,在我们从 1 元开始依次找零时,可以尝试一下当前要找零的面值(这里指 1元)是否能够被分解成另一个已求解的面值的找零需要的硬币个数再加上这一堆硬币中的某个面值之和,如果这样分解之后最终的硬币数是最少的,那么问题就得到答案了。

单是上面的文字描述太抽象,先假定以下变量:

values[] : 保存每一种硬币的币值的数组
valueKinds :币值不同的硬币种类数量,即values[]数组的大小
money : 需要找零的面值
coinsUsed[] : 保存面值为 i 的纸币找零所需的最小硬币数

算法描述:

 

[java] view plain copy 
  1. 当求解总面值为

  2.  i 的找零最少硬币数 coinsUsed[ i ] 时,将其分解成求解 coinsUsed[ i– cent

  3. s]和一个面值为cents元的硬币,由于 i– cents < i , 其解 coinsUsed[ i– cen

  4. ts] 已经存在,如果面值为 cents 的硬币满足题意,那么最终解 coinsUsed[ 

  5. i ] 则等于 coinsUsed[ i– cents] 再加上 1(即面值为 cents)的这一个硬币。  
  6.   
  7. 下面用代码实现并测试一下:
  8.   
  9. Java代码  
  10.   
  11.     public class CoinsChange {      
  12.         /**    
  13.          * 硬币找零:动态规划算法    
  14.          *     
  15.          * @param values   
  16.  
  17.          *            :保存每一种硬币的币值的数组    

  18.          * @param valueKinds    

  19.          *            :币值不同的硬币种类数量,即coinValue[]数组的大小 
  20.    
  21.          * @param money   
  22.  
  23.          *            :需要找零的面值  
  24.   
  25.          * @param coinsUsed    

  26.          *            :保存面值为i的纸币找零所需的最小硬币数    
  27.          */     
  28.         public static void makeChange(int[] values, int valueKinds, int money,  
  29.     
  30.   int[] coinsUsed) {      
  31.          
  32.             coinsUsed[0] = 0;      
  33.             // 对每一分钱都找零,即保存子问题的解以备用,即填表 
  34.      
  35.             for (int cents = 1; cents <= money; cents++) {      
  36.          
  37.                 // 当用最小币值的硬币找零时,所需硬币数量最多      
  38.                 int minCoins = cents;      
  39.          
  40.                 // 遍历每一种面值的硬币,看是否可作为找零的其中之一
  41.       
  42.                 for (int kind = 0; kind < valueKinds; kind++) {    
  43.                
  44.                     // 若当前面值的硬币小于当前的cents则分解问题并查表  
  45.     
  46.                     if (values[kind] <= cents) {    
  47.   
  48.                         int temp = coinsUsed[cents - values[kind]] + 1
  49.      
  50.                         if (temp < minCoins) {    
  51.   
  52.                             minCoins = temp;      
  53.                         }      
  54.                     }      
  55.                 }      
  56.                 // 保存最小硬币数 
  57.      
  58.                 coinsUsed[cents] = minCoins;      
  59.          
  60.                 System.out.println("面值为 " + (cents) + " 的最小硬币数 : " 
  61.     
  62.                         + coinsUsed[cents]);      
  63.             }      
  64.         }   
  65.    
  66.               
  67.         public static void main(String[] args) {      
  68.          
  69.             // 硬币面值预先已经按降序排列  
  70.     
  71.             int[] coinValue = new int[] { 25211051 };  
  72.     
  73.             // 需要找零的面值  
  74.     
  75.             int money = 63;      

  76.             // 保存每一个面值找零所需的最小硬币数,0号单元舍弃不用,所以要多加1      
  77.             int[] coinsUsed = new int[money + 1];      
  78.          
  79.             makeChange(coinValue, coinValue.length, money, coinsUsed);      
  80.         }      
  81.     }     
  82.   
  83.   
  84. 0/1背包问题的动态规划法求解,前人之述备矣,这里所做的工作,不过是自己根

  85. 据理解实现了一遍,主要目的还是锻炼思维和编程能力,同时,也是为了增进对

  86. 动态规划法机制的理解和掌握。  
  87.   
  88. 值得提及的一个问题是,在用 JAVA 实现时, 是按算法模型建模,还是用对象模

  89. 型建模呢? 如果用算法模型,那么 背包的值、重量就直接存入二个数组里;如果

  90. 用对象模型,则要对背包以及背包问题进行对象建模。思来想去,还是采用了对

  91. 象模型,尽管心里感觉算法模型似乎更好一些。有时确实就是这样,对象模型虽

  92. 然现在很主流,但也不是万能的,采用其它的模型和视角,或许可以得到更好的

  93. 解法。  
  94.   
  95. 背包建模:
  96.   
  97. Java代码  
  98.   
  99.     public class Knapsack {        
  100.               
  101.         /** 背包重量  */        
  102.         private int weight;   
  103.     
  104.         /** 背包物品价值  */  
  105.       
  106.         private int value;        
  107.         /***    
  108.          * 构造器    
  109.          */        
  110.         public Knapsack(int weight, int value) {        
  111.             this.value = value;        

  112.             this.weight = weight;        
  113.         }        

  114.         public int getWeight() {  
  115.       
  116.             return weight;   
  117.      
  118.         }        
  119.                 
  120.         public int getValue() {        
  121.             return value;    
  122.     
  123.         }        
  124.                 
  125.         public String toString() {     
  126.    
  127.             return "[weight: " + weight + " " + "value: " + value + "]";  
  128.         
  129.         }        
  130.     }        
  131.  
  132.         
  133.     import java.util.ArrayList;        
  134.           
  135.     /**    
  136.      * 求解背包问题:  
  137.   
  138.      * 给定 n 个背包,其重量分别为 w1,w2,……,wn, 价值分别为 v1,v2,……,vn 
  139.    
  140.      * 要放入总承重为 totalWeight 的箱子中,
  141.      
  142.      * 求可放入箱子的背包价值总和的最大值。
  143.     
  144.      * NOTE: 使用动态规划法求解 背包问题   
  145.  
  146.      * 设 前 n 个背包,总承重为 j 的最优值为 v[n,j], 最优解背包组成为 b[n];  
  147.   
  148.      * 求解最优值:    

  149.      * 1. 若 j < wn, 则 : v[n,j] = v[n-1,j];   
  150.  
  151.      * 2. 若  j >= wn, 则:v[n,j] = max{v[n-1,j], vn + v[n-1,j-wn]}。    
  152.      *     
  153.      * 求解最优背包组成: 
  154.    
  155.      * 1. 若 v[n,j] > v[n-1,j] 则 背包 n 被选择放入 b[n], 
  156.     
  157.      * 2. 接着求解前 n-1 个背包放入 j-wn 的总承重中,
  158.      
  159.      *    于是应当判断 v[n-1, j-wn] VS v[n-2,j-wn], 决定 背包 n-1 是否被选择。
  160.     
  161.      * 3. 依次逆推,直至总承重为零。    

  162.      *    重点: 掌握使用动态规划法求解问题的分析方法和实现思想。
  163.     
  164.      *    分析方法: 问题实例 P(n) 的最优解S(n) 蕴含 问题实例 P(n-1) 的最优解S(n-1);    
  165.      *              在S(n-1)的基础上构造 S(n)    
  166.  
  167.      *    实现思想: 自底向上的迭代求解 和 基于记忆功能的自顶向下递归    
  168.      */        
  169.     public class KnapsackProblem {        
  170.                 
  171.         /** 指定背包 */        
  172.         private Knapsack[] bags;        

  173.         /** 总承重  */   
  174.      
  175.         private int totalWeight;        
  176.                 
  177.         /** 给定背包数量  */  
  178.       
  179.         private int n;    
  180.    
  181.         /** 前 n 个背包,总承重为 totalWeight 的最优值矩阵  */   
  182.      
  183.         private int[][] bestValues;        
  184.                 
  185.         /** 前 n 个背包,总承重为 totalWeight 的最优值 */        
  186.         private int bestValue;   

  187.         /** 前 n 个背包,总承重为 totalWeight 的最优解的物品组成 */   
  188.      
  189.         private ArrayList<Knapsack> bestSolution;        
  190.                 
  191.         public KnapsackProblem(Knapsack[] bags, int totalWeight) {       
  192.             this.bags = bags;    
  193.     
  194.             this.totalWeight = totalWeight;
  195.             this.n = bags.length;  
  196.  
  197.             if (bestValues == null) {  

  198.                 bestValues = new int[n+1][totalWeight+1];  
  199.       
  200.             }        
  201.         }        
  202.  
  203.         /**    
  204.          * 求解前 n 个背包、给定总承重为 totalWeight 下的背包问题    
  205.          *     
  206.          */        
  207.         public void solve() {        
  208.                     
  209.             System.out.println("给定背包:");        
  210.             for(Knapsack b: bags) {  
  211.       
  212.                 System.out.println(b);   
  213.      
  214.             }        
  215.             System.out.println("给定总承重: " + totalWeight); 
  216.       
  217.             // 求解最优值    
  218.     
  219.             for (int j = 0; j <= totalWeight; j++) { 

  220.                 for (int i = 0; i <= n; i++) {                          
  221.                     if (i == 0 || j == 0) {    
  222.     
  223.                         bestValues[i][j] = 0;  
  224.       
  225.                     }          
  226.  
  227.                     else     
  228.     
  229.                     {    
  230.     
  231.                         // 如果第 i 个背包重量大于总承重,则最优解存在于前 i-1 个背包中,        
  232.                         // 注意:第 i 个背包是 bags[i-1]  
  233.       
  234.                         if (j < bags[i-1].getWeight()) {   
  235.      
  236.                             bestValues[i][j] = bestValues[i-1][j];  
  237.       
  238.                         } 
  239.                         else  
  240.        
  241.                         {        
  242.                             // 如果第 i 个背包不大于总承重,则最优解要么是包含第 i 个背包的最优解,        
  243.                             // 要么是不包含第 i 个背包的最优解, 取两者最大值,这里采用了分类讨论法        
  244.                             // 第 i 个背包的重量 iweight 和价值 ivalue 
  245.        
  246.                             int iweight = bags[i-1].getWeight();  
  247.       
  248.                             int ivalue = bags[i-1].getValue();  
  249.       
  250.                             bestValues[i][j] =   
  251.       
  252.                                 Math.max(bestValues[i-1][j], ivalue + bestValues[i-1][j-iweight]);   
  253.             } // else        
  254.                } //else     
  255.             
  256.                } //for    
  257.     
  258.             } //for   
  259.      
  260.                     
  261.             // 求解背包组成   
  262.      
  263.             if (bestSolution == null) {   
  264.      
  265.                 bestSolution = new ArrayList<Knapsack>();   
  266.      
  267.             }        
  268.             int tempWeight = totalWeight; 
  269.        
  270.             for (int i=n; i >= 1; i--) {        

  271.                if (bestValues[i][tempWeight] > bestValues[i-1][tempWeight]) { 
  272.        
  273.                    bestSolution.add(bags[i-1]);  // bags[i-1] 表示第 i 个背包  
  274.       
  275.                    tempWeight -= bags[i-1].getWeight();    
  276.     
  277.                }        
  278.                if (tempWeight == 0) { break; }   
  279.      
  280.             }        
  281.             bestValue = bestValues[n][totalWeight];  
  282.       
  283.         }        
  284.                 
  285.         /**    
  286.          * 获得前  n 个背包, 总承重为 totalWeight 的背包问题的最优解值 
  287.    
  288.          * 调用条件: 必须先调用 solve 方法    
  289.          *     
  290.          */        
  291.         public int getBestValue() {   
  292.       
  293.             return bestValue;        
  294.         }  
  295.      
  296.         /**    
  297.          * 获得前  n 个背包, 总承重为 totalWeight 的背包问题的最优解值矩阵    
  298.          * 调用条件: 必须先调用 solve 方法    
  299.          */        

  300.         public int[][] getBestValues() {        
  301.                     
  302.             return bestValues;        
  303.         }     
  304.     
  305.         /**    
  306.          * 获得前  n 个背包, 总承重为 totalWeight 的背包问题的最优解值矩阵 
  307.    
  308.          * 调用条件: 必须先调用 solve 方法
  309.          */        
  310.         public ArrayList<Knapsack> getBestSolution() {        
  311.             return bestSolution;  
  312.                 }  
  313.     
  1.                 
  2.     }       
  3.         
  4.     public class KnapsackTest {        
  5.               
  6.         public static void main(String[] args) {        
  7.                     
  8.             Knapsack[] bags = new Knapsack[] {        
  9.                     new Knapsack(2,13), new Knapsack(1,10),
  10.   
  11.                     new Knapsack(3,24), new Knapsack(2,15), 

  12.                     new Knapsack(4,28), new Knapsack(5,33),

  13.                     new Knapsack(3,20), new Knapsack(18

  14.             };  

  15.             int totalWeight = 10
  16.        
  17.             KnapsackProblem kp = new KnapsackProblem(bags, totalWeight);        
  18.                     
  19.             kp.solve();        
  20.             System.out.println(" -------- 该背包问题实例的解: --------- "); 
  21.        
  22.             System.out.println("最优值:" + kp.getBestValue()); 
  23.         
  24.             System.out.println("最优解【选取的背包】: ");   
  25.      
  26.             System.out.println(kp.getBestSolution());  
  27.       
  28.             System.out.println("最优决策矩阵表:"); 
  29.        
  30.             int[][] bestValues = kp.getBestValues();  
  31.       
  32.             for (int i=0; i < bestValues.length; i++) { 
  33.        
  34.                 for (int j=0; j < bestValues[i].length; j++) { 
  35.        
  36.                     System.out.printf("%-5d", bestValues[i][j]);  
  37.       
  38.                 }        
  39.                 System.out.println();    
  40.     
  41.             }        
  42.         }        
  43.     }   

原创粉丝点击