0-1背包-动态规划
问题描述
超市允许顾客使用一个可以承受重量为c的背包,选择一件或多件商品带走
每种商品有对应的价值和重量,如何带走总价最多的商品?
输入数据
第一行输入物品数量n和背包承重c。
接下来是n行数据,表示每个商品的价值和重量。
输出数据
一共2行
第1行输出最优解,即商品的最高总价。
第2行输出选择的商品编号。
输入输出样例
输入:
5 13
24 10
2 3
9 4
10 5
9 4
输出:
最优解为28 选择的商品为: 3 4 5
思路分析
构造备忘录f[i,c] ,表示在前i个商品中选择,背包容量为c时的最优解
对第i个商品进行分析
- 若选择商品i,价值为f[i-1,c-w[i] ] +v[i],即从前i-1个里面选择c减去当前商品重量w[i]的商品;
- 若不选商品i,价值为f[i-1,c],相当于从前i-1件商品里面选择重量为c的商品
由此可得递推公式 f[i,c]=max{ f[i-1,c-w[i]] +v[i] , f[i-1,c]}
接下来我们只需要逐步计算二维数组f中的各个数值,最后得到最优解f[n,c].
最优方案追踪
对于决策过程,另外定义一个rec二维数组,rec[i,c]的值为1说明此情况下的最优解包含商品i,为0则说明不包含。
最后回溯子问题可以得到选择的商品,定义flag标识数组,表示商品i是否在最优方案中
样例求解过程
这里Vi表示重量,Pi表示价值
最优解即为右下角的28.
Code
//
// main.cpp
// 0-1背包-动态规划法
//
// Created by MacBook Pro on 2021/3/30.
//
#include <iostream>
using namespace std;
void Knapsack(int v[], int w[], int n,int c,int **f,int **rec ,bool *flag)
{
int i;
for(i=1;i<=n;i++)
{
for(int j=1;j<=c;j++)
{
if(w[i]<=j&&v[i]+f[i-1][j-w[i]]>f[i-1][j])//当前商品重量小于背包容量且选择商品i价值更大
{
f[i][j]=v[i]+f[i-1][j-w[i]];
rec[i][j]=1;//记录选择
}
else
{
f[i][j]=f[i-1][j];
rec[i][j]=0;
}
}
}
//最优方案追踪
for(i=n;i>=1;i--)
{
if(rec[i][c]==1)//选择商品i
{
c-=w[i];//回溯子问题
flag[i]=1;//标记为选择
}
else
{
flag[i]=0;//标记为不选
}
}
}
int main()
{
int n,i;
cin>>n;
int c;//背包容量
cin>>c;
int v[n+1],w[n+1];
bool flag[n+1];//最优解是否包含商品i
for(i=1;i<=n;i++)
{
cin>>v[i]>>w[i];
}
int **f=new int *[n+1];
int **rec=new int *[n+1];
for(i=0;i<=n;i++)
{
f[i]=new int[c+1];
rec[i]=new int [c+1];
}
for(i=0;i<=c;i++)
{
f[0][i]=0;
rec[0][i]=0;
}
for(i=0;i<=n;i++)
{
f[i][0]=0;
rec[i][c]=0;
}
Knapsack(v,w,n,c,f,rec,flag);
cout<<"最优解为"<<f[n][c]<<endl;
cout<<"选择的商品为: ";
for(i=1;i<=n;i++)
{
if(flag[i])
cout<<i<<' ';
}
cout<<endl;
for(i=0;i<=n;i++)
delete []f[i];
delete []f;
return 0;
}