0/1背包
0/1背包的问题模型如下:
给定N个物品,其中第i个物品的体积为Vi ,价值为Wi 。有一容积为M的背包,要选择一些物品放入背包,使物品体积不超过M的前提下,物品的价值总和最大。
dp[i][j]表示从前i个物品中选出了总体积为j的物品放入背包,物品的最大价值。即我们很容易得出解决的代码:
memset(dp,0,sizeof(dp)); dp[0][0]=0; for(int i=1;i<=n;i++){ for(int j=0;j<=m;j++) dp[i][j]=dp[i-1][j]; for(int j=v[i];j<=m;j++) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]); }
根据上面的代码 我们可以看出 状态方程的推导只和当前状态以及上一状态有关。所以我们可以用滚动数组优化
memset(dp,0,sizeof(dp)); dp[0][0]=0; for(int i=1;i<=n;i++){ for(int j=0;j<=m;j++) dp[i&1][j]=dp[(i-1)&1][j]; for(int j=v[i];j<=m;j++) dp[i&1][j]=max(dp[i&1][j],dp[(i-1)&1][j-v[i]]+w[i]); }
这里我们只要2*m的空间即可
其实进一步分析代码,在每个阶段的开始,我们实际上只是执行了一次从dp[i-1][]到dp[i][]的一次拷贝,也就是说我们可以进一步省略掉dp数组的第一维
即dp[j]表示背包中放入总体积为j的最大价值
memset(dp,0,sizeof(dp)); dp[0]=0; for(int i=1;i<=n;i++){ for(int j=m;j>=v[i];j--) dp[j]=max(dp[j],dp[j-v[i]]+w[i]); }
我们应该可以注意到,我们的第二重循环采用了倒序的方式,这是为了满足0/1背包问题中每个物品是唯一的,只能放入一次的要求