问题描述
给定重量分别为,价值分别为的n件物品,和一个承重为W的背包。求这些物品中一个最有价值的子集,并能装到背包中。
蛮力法
背包问题的蛮力解法是穷举这些物品的所有子集,找出能够装到背包中的所有子集,并在这些子集中找出价值最大的子集。
//背包问题:蛮力法
#include <iostream>
#include <cstdio>
#define maxx 9999
using namespace std;
int n;//货物的总个数
int weight;//背包承受上限
int value = 0;//最大价值
int sum_w = 0;//重量
int sum_v = 0;//价值
int r[maxx];//存储子集
int count;
struct node{
int weight;//重量
int value;//价值
bool flag=false;//标记是否被挑选
};
node pack[maxx];
int recursion(int x)
{
//判断重量是否超过背包承受上限
if(sum_w > weight)
{
return 0;
}
//判断最大价值
if(sum_v > value)
{
value = sum_v;
count = 0;
//将最大价值所对应的子集放入r中
for(int i=0;i<n;i++)
{
if(pack[i].flag)
{
r[count++] = i;
}
}
}
for(int i=x;i<n;i++)
{
sum_v += pack[i].value;
sum_w += pack[i].weight;
pack[i].flag = true;
recursion(i+1);
//回溯
sum_v -= pack[i].value;
sum_w -= pack[i].weight;
pack[i].flag = false;
}
}
int main()
{
cout << "请输入货物的总数量以及背包能承受的最大重量:" << endl;
cin >> n >> weight;
cout << "请依次输入每件物品的重量和价值:" << endl;
for(int i=0;i<n;i++)
{
cin >> pack[i].weight >> pack[i].value;
}
recursion(0);
cout << "最大总价值的子集为: ";
for(int i=0;i<count;i++)
{
cout << r[i]+1 << " ";
}
cout << endl << "最大总价值最大可以为: " << value << endl;
return 0;
}
动态规划
- 基本思想
用weight[]数组存储每件物品的重量,value[]数组来存储每件物品的价值,用v[i][j]来表示i件物品在最大承受重量为j时的最大价值。
在背包问题中我们要思考的主要是选不选择这个物品。
对于一个物品,只有两种情况:
- 第i件不放进背包,这时总价值为v[i-1][j];
- 第i件放进背包,这时总价值为v[i][j-weight[i]]+value[i];
所以我们由此得出结论v[i][j] = max(v[i][j],v[i][j-weight[i]]+value[i]);
当背包容量为0时,v[i][0]=0;
当物品数量为0时,v[0][j]=0;
当然,在我们考虑是否放进去时,是有前提的。即要考虑物品的重量以及背包剩余容量。
- 当物品重量小于背包剩余容量时:
v[i][j] = max(v[i][j],v[i][j-weight[i]]+value[i]); - 当物品重量大于背包剩余容量时:
v[i][j] = v[i][j];
代码演示:
#include <iostream>
#include <cstdio>
#include <algorithm>
#define maxx 10000
using namespace std;
int n;//货物的总数量
int w;//背包能承受的最大重量
int value[maxx];//记录每件货物的价值
int weight[maxx];//记录每件货物的重量
int v[maxx][maxx];//记录一定承受重量对应的最大价值
int x[maxx];//记录最大价值时有哪些物品
int num = 0;
int main()
{
cout << "请输入货物的总数量以及背包能承受的最大重量:" << endl;
cin >> n >> w;
cout << "请依次输入每件物品的重量和价值:" << endl;
for(int i=0;i<n;i++)
{
cin >> weight[i] >> value[i] ;
}
//初始化dp表v[][]的第0列和第0行
for(int i=0;i<=n;i++)
{
v[i][0] = 0;
}
for(int i=0;i<=w;i++)
{
v[0][i] = 0;
}
//通过遍历判断i件物品在最大承受重量为j时的最大价值
for(int i=1;i<=n;i++)
{
for(int j=1;j<=w;j++)
{
if(j < weight[i])
{
v[i][j] = v[i-1][j];
}
else
{
v[i][j] = max(v[i-1][j],v[i-1][j-weight[i]]+value[i]);
}
}
}
//记录最大价值时有哪些物品
for(int i=n,j=w;i>0;i--)
{
if(v[i][j]>v[i-1][j])
{
x[num ++] = i;
j = j - weight[i];
}
}
//输出
cout << "最大总价值的子集为: ";
for(int i=num-1;i>=0;i--)
{
cout << x[i]+1 << " ";
}
cout << endl << "最大价值为: " << v[n][w]<< endl;
return 0;
}
运行结果如下:
当然,这个题也可以用一位数组来做:
//背包问题:动态规划
#include <iostream>
#include <cstdio>
#include <algorithm>
#define maxx 9999
using namespace std;
int n;
int weight[maxx];
int value[maxx];
int w;
int v[maxx];
int main()
{
cout << "请输入货物的总数量以及背包能承受的最大重量:" << endl;
cin >> n >> w;
cout << "请依次输入每件物品的重量和价值:" << endl;
for(int i=1;i<=n;i++)
{
cin >> weight[i] >> value[i];
}
for(int i=1;i<=n;i++)
{
for(int j=w;j>=weight[i];j--)
{
v[j] = max(v[j-weight[i]]+value[i],v[j]);
}
}
cout << "最大价值为: "<< v[w] << endl;
return 0;
}
运行结果如下: