背包问题和分割数组
- 有N件物品和一个重量为M的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使价值总和最大。
- 有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为两个子数组,子数组的元素个数不限,并使两个子数组之和最接近。
- 有一个无序、元素个数为2n的正整数数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组之和最接近。
- 有两个数组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