01背包问题(0-1 Knapsack Problem)
题意:先有价值为、重量为
的N个物品以及容量为W的背包。选择装入包中的需满足:
- 背包总价值尽可能得高
- 背包总重量不超过W
输入 第1行输入2个整数N,W,空格隔开。接下来N行输入第i个物品的价值与重量
,每个物品占一行,相邻数值之间空格隔开。
输出 输出总价值的最大值,占1行
限制 、
、
、
输入示例
4 5
4 2
5 2
2 1
8 3
输出
12
题解分析
“选”与“不选”对每次的值进行更新。首先、我们要准备一些变量
struct Item{
int v;
int w;
}A[MAX+1];//用来存放物品信息
int dp[MAX+1][10001];//dp[i][j]代表前i个物品在容量为j时的最大价值
//dp数组的初始化
for(int i=0;i<=n;i++){
for(int j=0;j<=W;j++)
dp[i][j]=0;
}
设第i的物品进入了是否放入容量为j背包的考虑范围:
"不选":,“选”:
,选的时候需要为第i个物品提供空间。
取最大值:。
for(int i=1;i<=n;i++){
for(int j=A[i].w;j<=W;j++)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-A[i].w]+A[i].v);
}
初始化的dp数组里的值:
v | w | 0 | 1 | 2 | 3 | 4 | 5 |
4 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
8 | 3 | 0 | 0 | 0 | 0 | 0 | 0 |
将A[1]放入容量由0到5的背包里:
v | w | 0 | 1 | 2 | 3 | 4 | 5 |
4 | 2 | 0 | 0 | 4 | 4 | 4 | 4 |
5 | 2 | 0 | |||||
1 | 2 | 0 | |||||
8 | 3 | 0 |
将A[2]放入容器由0-5的背包里:
v | w | 0 | 1 | 2 | 3 | 4 | 5 |
4 | 2 | 0 | 0 | 4 | 4 | 4 | 4 |
5 | 2 | 0 | 0 | 5 | 5 | 9 | 9 |
1 | 2 | 0 | |||||
8 | 3 | 0 |
最后形成:
v | w | 0 | 1 | 2 | 3 | 4 | 5 |
4 | 2 | 0 | 0 | 4 | 4 | 4 | 4 |
5 | 2 | 0 | 0 | 5 | 5 | 9 | 9 |
1 | 2 | 0 | 2 | 5 | 7 | 9 | 11 |
8 | 3 | 0 | 2 | 5 | 8 | 10 | 13 |
代码部分为:
#include<iostream>
#include<algorithm>
using namespace std;
#define MAX 100
#define WMAX 10001
struct Item{
int v;
int w;
}A[MAX+1];
int n,dp[MAX+1][10001];
int main(){
int n,W;//种数、总重
cin>>n>>W;
for(int i=1;i<=n;i++){
cin>>A[i].v>>A[i].w;
}
//dp数组的初始化
for(int i=0;i<=n;i++){
for(int j=0;j<=W;j++)
dp[i][j]=0;
}
for(int i=1;i<=n;i++){
for(int j=A[i].w;j<=W;j++)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-A[i].w]+A[i].v);
}
cout<<dp[n][W]<<endl;
return 0;
}
敲了这么多天的代码,代码质量水准还是一成不变(为了解题而解题)。我们可以记录下是哪些物品被挑选。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#define NMAX 105
#define WMAX 10005
#define DIAGONAL 1
#define TOP 0
struct Item{
int value;
int weight;
};
int N,W;
Item items[NMAX+1];
int C[NMAX+1][WMAX+1],G[NMAX+1][WMAX+1];
void compute(int &maxValue,vector<int>&selection){
for(int w=0;w<=W;w++){
C[0][w]=0;
G[0][W]=DIAGONAL;
}
for(int i=1;i<=N;i++)
C[i][0]=0;
for(int i=1;i<=N;i++){
for(int w=1;w<=W;w++){
//默认没选上
C[i][w]=C[i-1][w];
G[i][w]=TOP;
if(items[i].weight>w)
continue;
if(items[i].value+C[i-1][w-items[i].weight]>C[i-1][w]){
C[i][w]=C[i-1][w-items[i].weight]+items[i].value;
G[i][w]=DIAGONAL;
}
}
}
maxValue=C[N][W];
//完成统计操作
selection.clear();
for(int i=N,w=W;i>=1;i--){
if(G[i][w]==DIAGONAL){
selection.push_back(i);
w-=items[i].weight;
}
}
//追本溯源
reverse(selection.begin(),selection.end());
}
void input(){
cin>>N>>W;
for(int i=1;i<=N;i++){
cin>>items[i].value>>items[i].weight;
}
}
int main(){
int maxValue;
vector<int>selection;
input();
compute(maxValue,selection);
cout<<maxValue<<endl;
vector<int>::iterator it;
for(it=selection.begin();it!=selection.end();it++){
cout<<*(it)<<" ";
}
cout<<endl;
return 0;
}
加油!