问题描述
有n个物品,它们有各自的体积和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?
例如,有4件物品,背包可装物品的最大容量(bagv)为8,物品的容量和价值分别为2 3,3 4,4 5,5 6
用w代表物品的容量,用v代表物品的价值,如下表:
i(物品编号) | 1 | 2 | 3 | 4 |
---|---|---|---|---|
w(物品容量) | 2 | 3 | 4 | 5 |
v(物品价值) | 3 | 4 | 5 | 6 |
动态规划的核心就是填表,众多数据只需计算一次,保存表中,因此可以提高效率。
判断第i件物品可否放的下,就得考虑此时的j(当前背包状态)是否比w(i)要大。
找出递推关系式
- 若j<=w(i)(放不下):这时当下状态即为前一次的状态dp[i][j]=dp[i-1][j]
- 若j>=w(i)(放得下):此时需考虑放不放该物品两种选择的最优选择。
(1)若不放,dp[i][j]=dp[i-1][j]
(2)若放,dp[i][j]=dp[i-1][j-w[i]]+v[i]
即此时应:max(dp[i][j]=dp[i-1][j],dp[i][j]=dp[i-1][j-w[i]]+v[i])
有了这样的思想和关系式,就可以写出代码了
代码实现
#include <iostream>
using namespace std;
int main()
{
int maxn=0,n,bagv,i,j,w[500],v[500],dp[20][20]={{0}};
cin>>n>>bagv;
for(i=1;i<=n;i++)
{
cin>>w[i]>>v[i];
}
for(i=1;i<=n;i++)
{
for(j=1;j<=bagv;j++)
{
if(w[i]>j)
{
dp[i][j]=dp[i-1][j];
}
else {
dp[i][j]=max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]);
if(dp[i][j]>maxn)
{
maxn=dp[i][j];
}
}
}
}
for(i=0;i<=n;i++)
{
for(j=0;j<=bagv;j++)
cout<<dp[i][j]<<" ";//打印表格
cout<<endl;
}
cout<<maxn<<endl;
return 0;
}
背包问题最优解回溯
若想知道具体背包内最优条件下放了哪些东西,就可以通过回溯,通过同样的递推关系逆着查找判断.
- 如果dp[i][j]==dp[i-1][j],即第i个没有放入背包,再去查看上一个(i-1)
- 如果dp[i][j]==dp[i-1][j-w[i]]+v[i],即第i个放入了背包,标记此时的i,再去查看上一个(i-1)
直至i=1全部都经过判断
代码实现
#include <iostream>
using namespace std;
int item[20]={0};
int maxn=0,n,bagv,i,j,w[500],v[500],dp[20][20]={{0}};
void findwhat(int i,int j)
{
if(i>=1)
{
if(dp[i][j]==dp[i-1][j])//判断i放了没
{
item[i]=0;
findwhat(i-1,j);
}
else if(j>=w[i]&&dp[i][j]==dp[i-1][j-w[i]]+v[i])
{
item[i]=1;
findwhat(i-1,j-w[i]);
}
}
}
int main()
{
cin>>n>>bagv;
for(i=1;i<=n;i++)
{
cin>>w[i]>>v[i];
}
for(i=1;i<=n;i++)
{
for(j=1;j<=bagv;j++)
{
if(w[i]>j)
{
dp[i][j]=dp[i-1][j];
}
else {
dp[i][j]=max(dp[i-1][j-w[i]]+v[i],dp[i-1][j]);
if(dp[i][j]>maxn)
{
maxn=dp[i][j];
}
}
}
}
for(i=0;i<=n;i++)
{
for(j=0;j<=bagv;j++)
cout<<dp[i][j]<<" ";
cout<<endl;
}
cout<<maxn<<endl;
findwhat(n,bagv);
for(i=1;i<=n;i++)
if(item[i]==1)
cout<<i<<" ";
cout<<endl;
return 0;
}