一、题目
给定一个承重量为C的背包,n个重量分别为w1,w2,…,wn的物品,物品i放入背包能产生pi(>0)的价值(i=1,2,…,n)。
每个物品要么整个放入背包,要么不放。要求找出最大价值的装包方案。
输入格式:
输入的第一行包含两个正整数n和C(1≤n≤20),第二行含n个正整数分别表示n个物品的重量,第三行含n个正整数分别表示n个物品放入背包能产生的价值。
输出格式:
在一行内输出结果,包括最大价值装包方案的价值、具体装包方案,用空格隔开。具体装包方案是n个物品的一个子集,用长度为n的0、1串表示(1表示对应物品被选中,0表示没有被选中)。如果这样的0、1串不唯一,取字典序最大的那个串。
二、代码
/*
描述:0-1背包问题
日期:
思路:
1、获取最大价值:
dp[i][j]代表了,在前i个物品内,拿限重为j的最大价值
(1)状态转移方程:dp[i][j]=max[ 1),2)]
1)选i物品:dp[i][j]=value[i]+dp[i-1][j-weigh[i]]
选了i物品,那么首先加上value[i],再者,还可以加上,前i-1个的情况下的
限重为j-weigh[i]的物品,也即dp[i-1][j-weigh[i]]
2)不选i物品:dp[i][j]=dp[i-1][j]
物品的限制减一,限重不变,也即dp[i-1][j]
3)然后在1)和2)中取最大的
4)有个陷阱,1)中的dp[i-1][j-weigh[i]],j-weigh[i]可能会越界,所以要加一个if判断
2、获取选择
利用回溯法
(1)循环n次,就是循环n个物品,循环体中询问是否dp[i][j]==dp[i-1][j]
1)若是等于,则说明第i个物品,有它没它都一样,所以不选
2)若是不等于,则说明第i个物品至关重要,所以要选上,并且选了之后限重会变小
所以j-=weigh[i]
(2)题目中要求输出字典序最大的结果,我们在求解最大价值的时候,
也是按照前面的物品先放入背包,当且仅当后面物品放入的时候产生影响也考虑放入,
所以我们回溯的时候得出的结果就是本题需要的答案。
*/
#include<iostream>
#include <vector>
using namespace std;
vector<int>weigh;
vector<int>value;
vector<vector<int>>dp(100,vector<int>(100,0));
string choice="000000000000000000000000000";
void dp_opt(int n,int bag_weigh)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=bag_weigh;j++)
{
//max(选i物品,不选i物品)
if(j>=weigh[i])
{
dp[i][j]=max(value[i]+dp[i-1][j-weigh[i]],dp[i-1][j]);
}
else dp[i][j]=dp[i-1][j];
}
}
return;
}
void get_choice(int i,int j)
{
while(i>=1)
{
if(dp[i][j]==dp[i-1][j])
//第i个物品不重要
choice[i]='0';
else
//第i个物品很重要
{
choice[i]='1';
j-=weigh[i];
}
i--;
}
return;
}
int main()
{
//输入数据物品数量、背包最大承重
int n,bag_weigh;
cin>>n>>bag_weigh;
//因为dp数组的第0行和第0列是没有数据的,为0,所以这里统一
weigh.push_back(0);
value.push_back(0);
//输入物品重量以及价值
for(int i=0;i<n;i++)
{
int w;
cin>>w;
weigh.push_back(w);
}
for(int i=0;i<n;i++)
{
int v;
cin>>v;
value.push_back(v);
}
//获得最大收益
dp_opt(n,bag_weigh);
//获取选择
get_choice(n,bag_weigh);
//输出结果
cout<<dp[n][bag_weigh]<<" ";
for(int i=1;i<=n;i++)
cout<<choice[i];
return 0;
}