0-1背包问题是典型的动态规划问题,需要求解的是,为了体积V的背包中物体总价值最大化,N件物品中第i件应该放入背包中吗?(其中每个物品最多只能放一件)。
思路:定义一个二维数组f[N][V],其中每个元素代表一个状态,即前i个物体中若干个放入体积为V背包中最大价值。其中f[i][j]表示前i件中若干个物品放入体积为j的背包中的最大价值。 分别对i和j进行循环,从小到大逐步计算,其中,大问题依赖于小问题的求解。
初始状态:f[0][0...V]和f[0...N][0]都为0,前者不放任何物体,不管背包体积是多少,价值都为0;后者表示背包体积为0,放不来任何物体,价值也肯定都是0。
转移函数:
if (背包体积j小于物品i的体积) f[i][j] = f[i-1][j] //背包装不下第i个物体,目前只能靠前i-1个物体装包
else f[i][j] = max(f[i-1][j], f[i-1][j-Vi] + Wi) //不装当前物体,或装当前物体,选价值最大者
示例代码:
#include<iostream> using namespace std; int main() { int nArr[6][13] = {{0}}; int nCost[6] = {0 , 2 , 5 , 3 , 10 , 4}; //花费 int nVol[6] = {0 , 1 , 3 , 2 , 6 , 2}; //物体体积 int bagV = 12; for( int i = 1; i< sizeof(nCost)/sizeof(int); i++) { for( int j = 1; j<=bagV; j++) { if(j<nVol[i]) nArr[i][j] = nArr[i-1][j]; else nArr[i][j] = max(nArr[i-1][j] , nArr[i-1][j-nVol[i]] + nCost[i]); cout<<nArr[i][j]<<' '; } cout<<endl; } cout<<nArr[5][12]<<endl; return 0; }
循环过程如下图,从左上角开始,一行一行往后,直到右下角
更深入的,可能会有更多维的问题,如有多个“体积”(即多个限制条件)。如下面的问题:
给定一个字符串数组,每个字符串由0和1组成,给定两个常数m和n,m表示拥有的0的个数,n表示拥有的1的个数,求用这m个0和n个1,最多可以组成数组中的字符串的最大个数。
这个问题就有两个限制条件:0的个数和1的个数。不过思路相似,只是多了一重循环。
public int findMaxForm(String[] strs, int m, int n) { int l = strs.length; int[][][] dp = new int[l+1][m+1][n+1]; for (int i = 0; i < l+1; i++) { int[] nums = new int[]{0,0}; if (i > 0) { nums = calculate(strs[i-1]); } for (int j = 0; j < m+1; j++) { for (int k = 0; k < n+1; k++) { if (i == 0) { dp[i][j][k] = 0; } else if (j>=nums[0] && k>=nums[1]) { dp[i][j][k] = Math.max(dp[i-1][j][k], dp[i-1][j-nums[0]][k-nums[1]]+1); } else { dp[i][j][k] = dp[i-1][j][k]; } } } } return dp[l][m][n]; } private int[] calculate(String str) { int[] res = new int[2]; Arrays.fill(res, 0); for (char ch : str.toCharArray()) { if (ch == '0') { res[0]++; } else if (ch == '1') { res[1]++; } } return res; }