分享

算法设计与分析 3.9 0-1背包问题

 shaobin0604@163.com 2006-09-02
  1. /**********************************************************
  2. *        3.9        0-1背包问题
  3. *
  4. *        问题描述:
  5. *                给定n种物品和一背包。物品 i 的重量是 w[i] ,其价值为
  6. *        v[i] ,背包的容量为 c 。问:应该如何选择装入背包中的物品,
  7. *        使得装入背包中物品的总价值最大?
  8. *                
  9. *                在选择装入背包中的物品时,对每种物品 i 只有两种选择,
  10. *        即装入或不装入背包。不能将物品 i 装入背包多次,也不能只
  11. *        装入部分的物品 i 。因此,该问题称为 0-1 背包问题。
  12. *
  13. *                此问题的形式化描述为,给定 c > 0, w[i] > 0, v[i] > 0
  14. *        1 <= i <= n ,要求找出 n 元 0-1 向量 x[1 .. n], 其中x[i]
  15. *        等于0或1,使得对 w[i] * x[i] 求和小于等于 c ,并且 v[i] * 
  16. *        x[i]达到最大。因此,0-1背包问题是一个特殊的整数规划问题。
  17. *
  18. ***********************************************************/
  19.  
  20.  
  21. public class BagZeroOne {
  22.  
  23.         /**********************************************************************
  24.         *                        动态规划解 (Dynamic Programming)
  25.         *        
  26.         *        @param v        in        the 物品价值数组
  27.         *        @param w        in        the 物品重量数组
  28.         *        @param c        in        the 背包的容量
  29.         *        @param m        out        the 最优值数组,m[i][j]即背包容量为j,可选物品为 i, i + 1, ... , n 时0-1背包问题的最优值
  30.         *        
  31.         *        由于0-1背包问题的最优子结构性质,可以建立计算m[i][j]的递归式如下:
  32.         *                         / max{m[i + 1][j]), m[i + 1][j - w[i]] + v[i]}                j >= w[i]        
  33.         *        m[i][j]       /
  34.         *                        \
  35.         *                          \ m[i + 1][j]                                                         0 <= j < w[i]
  36.         *
  37.         *                        / v[n]                                                                    j >= w[n]
  38.         *        m[n][j]    /
  39.         *                      \
  40.         *                        \ 0                                                                        0 <= j < w[n]
  41.         *
  42.         **********************************************************************/
  43.     public static void knapsack(int[] vint[] wint cint[][] m) {
  44.                 int n = v.length - 1;
  45.                 int jMax = Math.min(w[n] - 1c);
  46.                 for (int j = 0j <= jMaxj++) {
  47.                     m[n][j] = 0;
  48.                 }
  49.                 for (int j = w[n]j <= cj++) {
  50.                     m[n][j] = v[n];
  51.                 }
  52.                 for (int i = n - 1i > 0i--) {
  53.                     jMax = Math.min(w[i] - 1c);
  54.                         for (int j = 0j <= jMaxj++) {
  55.                             m[i][j] = m[i + 1][j];
  56.                         }
  57.                         for (int j = w[i]j <= cj++) {
  58.                             m[i][j] = Math.max(m[i + 1][j]m[i + 1][j - w[i]] + v[i]);
  59.                         }
  60.                 }
  61.                 /*
  62.                 m[1][c] = m[2][c];
  63.                 if (c >= w[1]) {
  64.                     m[1][c] = Math.max(m[1][c], m[2][c - w[1]] + v[1]);
  65.                 }
  66.                 */
  67.         }
  68.  
  69.         /**
  70.         *        @param        m        in        the 最优值数组
  71.         *        @param        w        in        the 重量数组         
  72.         *        @param        c        in        the 背包容量
  73.         *        @param        x        out        the 物品选择数组 if x[i] == 1 则选物品 i, 否则不选
  74.         **/
  75.         public static void trackback(int[][] mint[] wint cint[] x) {
  76.                 int n = w.length - 1;
  77.                 for (int i = 1i < ni++) {
  78.                     if (m[i][c] == m[i + 1][c]) {
  79.                         x[i] = 0;                //不选物品 i 
  80.                     } else {
  81.                         x[i] = 1;                //选择物品 i
  82.                                 c -= w[i];                //剩余容量
  83.                     }
  84.                 }
  85.                 x[n] = (m[n][c] > 0)10;
  86.         }
  87.  
  88.         public static void testDynamicProgramming() {
  89.                 System.out.print("1. --- testDynamicProgramming      ---> ");
  90.                 //输入
  91.                 int c = 7;
  92.                 int[] w = {05321};
  93.                 int[] v = {04431};
  94.                 
  95.                 //应该的输出
  96.                 int expectedVMax = 8;
  97.                 int[] expectedX = {00111};
  98.                 
  99.                 //程序运行时变量
  100.                 int[][] m = new int[w.length][c + 1];
  101.                 int[] x = new int[w.length];
  102.  
  103.  
  104.                 knapsack(vwcm);
  105.                 trackback(mwcx);
  106.                 
  107.                 if (m[1][c] == expectedVMax && java.util.Arrays.equals(xexpectedX)) {
  108.                     System.out.println("Test success!");
  109.                 } else {
  110.                     System.out.println("Test fail!");
  111.                 }
  112.         }
  113.  
  114.         /******************************************************************************
  115.         *                        暴力解 (Brutal Force)
  116.         *
  117.         *        物品 i 的重量 w[i], 价值 v[i]
  118.         *        
  119.         *        递归算法
  120.         *                try (物品 i, 当前选择已经达到的重量之和 tw, 本方案可能达到的总价值 tv) 
  121.         *                {
  122.         *                        //考虑物品 i 包含在当前方案的可能性
  123.         *                        if (包含物品 i 是可接受的)
  124.         *                        {
  125.         *                                将物品 i 包含在当前方案中;
  126.         *                                if (i < n - 1) 
  127.         *                                        try(i + 1, tw + w[i], tv);
  128.         *                                else        //又是一个完整方案,因它比前面的方案要好,以它作为最佳方案
  129.         *                                        以当前方案作为临时最佳方案保存
  130.         *                                恢复物品 i 不包含在内的状态
  131.         *                        }
  132.         *                        //考虑物品 i 不包含在当前方案中的可能性
  133.         *                        if (不包含物品 i 仅是可考虑的)
  134.         *                        {
  135.         *                                if (i < n - 1)
  136.         *                                        try(i + 1, tw, tv - v[i]);
  137.         *                                else        //又是一个完整方案,因它比前面的方案要好,以它作为最佳方案
  138.         *                                        以当前方案作为临时最佳方案保存
  139.         *                        }
  140.         *                }
  141.         ******************************************************************************/
  142.  
  143.         private static int[] w;                        //重量
  144.         private static int[] v;                        //价值
  145.         private static int[] x;                        //最优解
  146.         private static int[] opt;                //有效解
  147.         private static int c;                        //背包容量
  148.         private static int maxv;                //最优值
  149.  
  150.         public static void find(int iint twint tv) {
  151.                 //考虑物品 i 包含在当前方案中的可能性
  152.                 if (tw + w[i] <= c) {        //包含物品 i 是可以接受的
  153.                     opt[i] = 1;
  154.                         if (i < w.length - 1) {
  155.                             find(i + 1tw + w[i]tv);
  156.                         } else {                        //又是一个完整方案,因它比前面的方案好,以它作为最佳方案
  157.                             for (int j = 0j < x.lengthj++) {
  158.                                 x[j] = opt[j];
  159.                             }
  160.                                 maxv = tv;
  161.                         }
  162.                         opt[i] = 0;
  163.                 }
  164.                 //考虑物品 i 不包含在当前方案中的可能性
  165.                 if (tv - v[i] > maxv) {        //不包含物品 i 是可以考虑的
  166.                     if (i < w.length - 1) {
  167.                         find(i + 1twtv - v[i]);
  168.                     } else { //又是一个完整方案,因它比前面的方案好,以它作为最佳方案
  169.                         for (int j = 0j < x.lengthj++) {
  170.                             x[j] = opt[j];
  171.                         }
  172.                                 maxv = tv - v[i];
  173.                     }
  174.                 }
  175.         }
  176.  
  177.         public static void testBrutalForceRecursive() {
  178.                 System.out.print("2. --- testBrutalForceRecursive    ---> ");
  179.                 int[] expectedX = {0111};
  180.                 int        expectedMaxV = 8;
  181.  
  182.                 w = new int[] {5321};
  183.                 v = new int[] {4431};
  184.                 x = new int[w.length];
  185.                 opt = new int[w.length];
  186.                 c = 7;
  187.                 int tv = 0;
  188.                 for (int i : v) {
  189.                         tv += i;    
  190.                 }
  191.  
  192.                 find(00tv);                
  193. //                System.out.println("maxv = " + maxv);
  194. //                System.out.println("x = " + java.util.Arrays.toString(x));
  195.                 if (maxv == expectedMaxV && java.util.Arrays.equals(xexpectedX)) {
  196.                     System.out.println("Test success!");
  197.                 } else {
  198.                     System.out.println("Test fail!");
  199.                 }
  200.         }
  201.  
  202.         /****************************************************************
  203.         *                暴力解 (Brutal Force)
  204.         *        
  205.         *        非递归算法
  206.         *
  207.         *
  208.         *****************************************************************/
  209.  
  210.         //当前候选解中各物品的考虑和选择状态,以及置该物品候选解的状态
  211.         private static int[] flag;                                //物品的考虑状态:0.不选;1.将被考虑;2.曾被选中
  212.         private static int[] twe;                                //已经达到的总重量
  213.         private static int[] tve;                                //期望的总价值
  214.  
  215.         private static int maxw;                                //背包容量
  216.         private static int[] cop;                                //临时最佳解的物品选择方案,当cop[i] 为 1 时,物品 i 在解中
  217.  
  218.         //将考虑物品 i 
  219.         private static void next(int iint twint tv) {
  220.                 flag[i] = 1;
  221.                 twe[i] = tw;
  222.                 tve[i] = tv;
  223.         }
  224.         public static int find(int[] wint[] vint n) {
  225.                 int ikf;
  226.                 int maxvtwtvtotv = 0;
  227.                 maxv = 0;
  228.                 for (int value : v) {
  229.                     totv += value;
  230.                 }
  231.                 next(00totv);
  232.                 i = 0;
  233.  
  234.                 while (i >= 0) {
  235.                     f = flag[i];
  236.                         tw = twe[i];
  237.                         tv = tve[i];
  238.                         switch (f) {
  239.                                 case 0:                                                                                                        //回退
  240.                                         i--;
  241.                                         break;
  242.  
  243.                             case 1:                                                                                                        //考虑被选中
  244.                                 flag[i]++;
  245.                                         if (tw + w[i] <= maxw) {                                                        //选中可行吗?
  246.                                             if (i < n - 1) {
  247.                                                 next(i + 1tw + w[i]tv);
  248.                                                         i++;
  249.                                             } else {
  250.                                                 maxv = tv;
  251.                                                         for (k = 0k < nk++) {
  252.                                                             cop[k] = ((flag[k] != 0)1 : 0);
  253.                                                         }
  254.                                             }
  255.                                         }
  256.                                 break;
  257.  
  258.                             default:                                                                                                //flag等于2
  259.                                 flag[i] = 0;
  260.                                         if (tv - v[i] > maxv) {                                                                //不选物品 i 可行吗?
  261.                                             if (i < n - 1) {
  262.                                                 next(i + 1twtv - v[i]);
  263.                                                         i++;
  264.                                             } else {
  265.                                                 maxv = tv - v[i];
  266.                                                         for (k = 0k < nk++) {
  267.                                                             cop[k] = ((flag[k] != 0)1 : 0);                                                            
  268.                                                         }
  269.                                             }
  270.                                         }
  271.                                 break;
  272.                         }
  273.                 }
  274.                 return maxv;
  275.         }
  276.  
  277.         public static void testBrutalForceNotRecursive() {
  278.                 System.out.print("3. --- testBrutalForceNotRecursive ---> ");
  279.                 int[] expectedX = {0111};
  280.                 int expectedMaxV = 8;
  281.                 
  282.                 int[] w = new int[] {5321};
  283.                 int[] v = new int[] {4431};
  284.                 int n = w.length;
  285.                 
  286.                 cop = new int[n];
  287.  
  288.                 flag = new int[n];
  289.                 twe = new int[n];
  290.                 tve = new int[n];
  291.  
  292.                 maxw = 7;
  293.  
  294.                 int maxv = find(wvn);                
  295. //                System.out.println("maxv = " + maxv);
  296. //                System.out.println("x = " + java.util.Arrays.toString(x));
  297.                 if (maxv == expectedMaxV && java.util.Arrays.equals(copexpectedX)) {
  298.                     System.out.println("Test success!");
  299.                 } else {
  300.                     System.out.println("Test fail!");
  301.                 }
  302.  
  303.         }
  304.  
  305.         public static void main(String[] args) {
  306.                 testDynamicProgramming();
  307.                 testBrutalForceRecursive();
  308.                 testBrutalForceNotRecursive();
  309.         }
  310. }

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多