46.携带研究材料
思路:0-1背包问题。确定dp数组及其下标的含义。dp[i][j]表示向空间大小为j的背包里放0~i个物品的最大价值。递推公式dp[i][j] = max(dp[i-1][j],dp[i-1][j-space[i]]+value[i]),有两种情况,不放i进背包,放i进背包,放i进背包的话,背包至少为i空余处space[i]的位置。初始化dp数组,dp[i][0]=0,因为背包容量为0时,什么都放不进去,价值为0。dp[0][i] = value[i] i>=space[i]。当背包容量大于第一个物品的容量时价值为第一个物品的价值。当前位置的价值只与上左方向位置的价值有关,因此遍历顺序为从左到右从上到下。打印dp数组,可以用于debug。
#include<iostream>
using namespace std;
#include<vector>
int maxvalue(vector<int>& space,vector<int>& value,int N,int M)
{
//确定dp数组及其下标的含义 dp[i][j]代表0~i号物品放入空间j的最大价值
//递推公式 dp[i][j] = max( dp[i-1][j],dp[i-1][j-space[i]]+value[i]);
//初始化dp数组 dp[i][0] = 0; dp[0][j] = value[0]; j>=space[0];
//遍历顺序 从左到右 从上到下 当前值取决于左上值
//打印dp数组,用于debug
vector<vector<int>> dp(M,vector<int>(N+1,0));
for(int j = space[0];j<=N;j++)
{
dp[0][j] = value[0];
}
for(int i = 1;i<M;i++)
{
for(int j = 0;j<=N;j++)
{
if(j<space[i])
{
dp[i][j] = dp[i-1][j];
}
else
{
dp[i][j] = max( dp[i-1][j],dp[i-1][j-space[i]]+value[i]);
}
}
}
return dp[M-1][N];
}
int main()
{
int M;
int N;
cin>>M;
cin>>N;
vector<int> space(M);
vector<int> value(M);
for(int i =0;i<M;i++)
{
cin>>space[i];
}
for(int i = 0;i<M;i++)
{
cin>>value[i];
}
cout<<maxvalue(space,value,N,M);
system("pause");
return 0;
}
思路:滚动数组解法,用一维dp数组,但value的循环需要倒序,因为要保证每个物品只用一次。
#include<iostream>
using namespace std;
#include<vector>
int bag(int M,int N,vector<int> &space,vector<int> &value)
{
//确定dp数组及其下标含义 dp[j]表示背包j容量可以携带的最大价值
//递推函数 dp[j] = max(dp[j],dp[j-space[i]+value[i]);
//初始化dp数组 vector<int>dp(M,0); 假设价值最小为0
vector<int> dp(N+1,0);
for(int i = 0;i<M;i++)
{
for(int j = N;j>=space[i];j--)
{
dp[j] = max(dp[j],dp[j-space[i]]+value[i]);
}
}
return dp[N];
}
int main()
{
int M;
int N;
cin>>M;
cin>>N;
vector<int> space(M,0);
vector<int> value(M,0);
for(int i=0;i<M;i++)
{
cin>>space[i];
}
for(int j = 0;j<M;j++)
{
cin>>value[j];
}
cout<< bag(M,N,space,value);
system("pause");
return 0;
}
416.分割等和子集
思路:如果子集能分为两个子集,两个子集的元素和相等,则返回true。 可以考虑成01背包问题,集合中的每个元素的值即代表空间也代表价值。确定dp数组及其下标含义,dp[j]代表空间为j的背包的最大价值。递推公式,dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]); 初始化dp数组,价值为非零整数,初始化为0.遍历顺序,从后往前,因为每个元素只能用一次,打印dp数组,用于debug。当dp[target] = target。说明有子集满足和为sum/2。
class Solution {
public:
bool canPartition(vector<int>& nums) {
//利用动态规划求解,考虑成一个01背包问题
//确定dp数组及其下标含义 dp[j] 为空间为j的背包所能装的最大价值
//递推公式 dp[j] = max( dp[j], dp[j-space[i]]+vaule[i])
//初始化dp数组 最小价值为一个不为负的整数
//打印dp数组,用于debug
int sum=0;
for(int i =0;i<nums.size();i++)
{
sum+=nums[i];
}
if(sum%2==1)
{
return false;
}
else
{
int target = sum/2;
vector<int> dp(target+1);
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;
}
};
收获
一维dp数组倒序。