看了题解。dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。状态转移方程:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
要统计选那些物体,背包重量,物体价值,假设物体价值作为结果,那么需要二维数组。
还有要注意初始化:
dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。
当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
#include<bits/stdc++.h>
using namespace std;
int n,bg;
void run(){
vector<int> weight(n,0);
vector<int> value(n,0);
for(int i=0;i<n;i++){
cin>>weight[i];
}
for(int i=0;i<n;i++){
cin>>value[i];
}
vector<vector<int>> dp(weight.size(),vector<int>(bg+1,0));
for(int i=weight[0];i<=bg;i++){
dp[0][i]=value[0];
}
for(int i=1;i<n;i++){
for(int j=0;j<=bg;j++){
if(weight[i]>j) {
dp[i][j]=max(dp[i][j],dp[i-1][j]);
}
else
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]); //注意能放得下不代表要放
}
}
cout<<dp[weight.size()-1][bg]<<endl;
}
int main(){
while(cin>>n>>bg){
run();
}
return 0;
}
上面题目改为用滚动数组解决。滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。在一维dp数组中,dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]。二维dp遍历的时候,背包容量是从小到大,而一维dp遍历的时候,背包是从大到小:为了避免覆盖。再来看看两个嵌套for循环的顺序,代码中是先遍历物品嵌套遍历背包容量,那可不可以先遍历背包容量嵌套遍历物品呢?不可以。
#include<bits/stdc++.h>
using namespace std;
int n,bg;
void run(){
vector<int> weight(n,0);
vector<int> value(n,0);
for(int i=0;i<n;i++){
cin>>weight[i];
}
for(int i=0;i<n;i++){
cin>>value[i];
}
vector<int> dp(bg+1,0);
for(int i=0;i<n;i++){
for(int j=bg;j>=weight[i];j--){
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]); //注意能放得下不代表要放
}
}
cout<<dp[bg]<<endl;
}
int main(){
while(cin>>n>>bg){
run();
}
return 0;
}
我原本的想法是分成两个数组,那么需要一个数组为加,另一个为减,加起来为0就行。但是不太好写成动态规划。
其实可以看成是物体的重量和价值是一样的,我没想到的是要凑成总和的一半,实际上是不可能大于总和的一半的,所以也就是可以求最大。转化为动态规划。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum=0;
for(int i=0;i<nums.size();i++){
sum+=nums[i];
}
vector<int> dp(10001,0);
if(sum%2!=0) return false;
int target=sum/2;
for(int i=0;i<nums.size();i++){
for(int j=target;j>=nums[i];j--){
dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
}
}
if(dp[target]==target) return true;
return false;
}
};