背包问题和分割数组

  1. 有N件物品和一个重量为M的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。
  2. 有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为两个子数组,子数组的元素个数不限,并使两个子数组之和最接近。
  3. 有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组之和最接近。
  4. 有两个数组a,b,大小都为n,数组元素的值任意整形数,无序,要求:通过交换a,b中的元素,使[数组a元素的和]与[数组b元素的和]之间的差最小。

1、思路:

  f(i,j)表示前i个物品取任意个放入容量为j的背包中的最大价值。f(i,j)=max{f(i-1,j),f(i-1,j-w[i])+v[i]},第i个物品可以选择放入背包或不放入背包。

 1 int Bag01(int* w, int* v, int n, int b)
 2 {
 3     int** dp = new int*[n + 1];
 4     for (int i = 0; i <= n; i++)
 5         dp[i] = new int[b + 1];
 6     for (int i = 0; i <= n; i++)
 7         cout << dp[i][0] << endl;
 8     for (int j = 0; j <= b; j++)
 9         dp[0][j] = 0;
10     for (int i = 1; i <= n; i++)
11     {
12         for (int j = 1; j <= b; j++)
13         {
14             //if (j < w[i]),错的原因是dp的第i个对应w和v的第i-1个。
15             if (j < w[i -1])
16                 dp[i][j] = dp[i - 1][j];
17             else
18                 //dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
19                 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]);
20         }
21     }
22     int result = dp[n][b];
23     for (int i = 0; i <= n; i++)
24         delete[] dp[i];
25     delete[] dp;
26     return result;
27 }

 

2、思路:

  f(i,j)表示前i个数取任意个求和,和为j是否存在。f(i,j)=f(i-1,j) || f(i-1, j-data[i]),第i个数可以选择是否参与计算。

 1 int DivideArray1(int data[], int len)
 2 {
 3     int i, j, sum = 0, half;
 4     for (i = 0; i < len; i++)
 5         sum += data[i];
 6     half = sum / 2;
 7     int **dp = new int*[len + 1];
 8     for (i = 0; i <= len; i++)
 9         dp[i] = new int[half + 1];
10     dp[0][0] = true;
11     for (j = 1; j <= half; j++)
12         dp[0][j] = false;
13     for (i = 1; i <= len; i++)
14     {
15         for(j = 1; j <= half; j++)
16         {
17             if (j < data[i - 1])
18                 dp[i][j] = dp[i - 1][j];
19             else
20                 dp[i][j] = dp[i - 1][j] || dp[i - 1][j - data[i - 1]];
21         }
22     }
23     for (j = half; j >= 1 && !dp[len][j]; j--);
24     return sum - 2 * j;
25 }

 

3、思路:

  f(i,j)表示有技巧地(迭代判断是否包含第k个数)取i个数,和为j是否存在。这里和第一个状态表示不一样,请注意!

 1 int DivideArray2(int data[], int len)
 2 {
 3     int i, j, k, sum = 0, half;
 4     for (i = 0; i < len; i++)
 5         sum += data[i];
 6     half = sum / 2;
 7     int **dp = new int*[len + 1];
 8     for (i = 0; i <= len; i++)
 9         dp[i] = new int[half + 1];    
10     for (i = 0; i <= len; i++)
11     {
12         for (j = 0; j <= half; j++)
13             dp[i][j] = false;
14     }
15     dp[0][0] = true;
16     for (k = 1; k <= len; k++)
17     {
18         for(i = min(k, len / 2); i >= 1; i--)
19         {
20             for (j = 1; j <= half; j++)
21             {
22                 if (j >= data[k - 1] && dp[i - 1][j - data[k - 1]])
23                     dp[i][j] = true;
24             }
25         }
26     }
27     for (j = half; j >= 1 && !dp[len / 2][j]; j--);
28     return sum - 2 * j;
29 }

   另一种更为容易理解的解法详见另一位好友的博客:http://www.cnblogs.com/liyukuneed/archive/2013/05/27/3090454.html

 

posted on 2013-03-25 20:42  月moon鸟  阅读(505)  评论(0编辑  收藏  举报

导航