AcWing 12. 背包问题求具体方案
- AcWing 12. 背包问题求具体方案
- (1)问题
- (2)分析
- (3)代码
AcWing 12. 背包问题求具体方案
(1)问题
(2)分析
我们先看一下这道题中最后要的答案是一个字典序最小的答案。因此我们从小到大遍历每个物品,如果碰到一个物品可选可不选,那么我们一定选,因为我们是从小到大遍历的,所以后遍历的物品的序号肯定大,我们就无法保证字典序最小了。
那么现在的关键是我们要保证从小到大遍历物品。
但是在作者之前的文章中写过一篇关于机器分配(分组背包与方案数)的文章。在这篇文章中我讲解过输出方案的思路。我们从小到大推导可以得到最终的答案,但是我们想要得到一个方案的话,需要倒过来遍历,即从大到小。至于为什么这样做,作者在机器分配这一题的文章中利用拓扑图做了一定的解释。
那么这道题我们也需要反过来遍历。
所以为了满足这道题从小到大解决最小字典序的问题的话,就要更改一下状态的定义。
f [ i ] [ j ] f[i][j] f[i][j]表示从i–n的物品里面选,在背包容量为j的条件下,选出最大价值。由于DP问题是按照规模从小到大计算的,所以i要从n到1枚举。这样做的好处就是,我们讨论方案的时候,将其反过来以后,就是从1到n。即从小到大。
(3)代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1010;
int v[N],w[N],f[N][N],cnt[N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%d%d",v+i,w+i);
for(int i=n;i>=1;i--)
{
for(int j=0;j<=m;j++)
{
f[i][j]=f[i+1][j];
if(j>=v[i])f[i][j]=max(f[i][j],f[i+1][j-v[i]]+w[i]);
}
}
int j=m;
int count=0;
for(int i=1;i<=n;i++)
if(j>=v[i]&&f[i+1][j-v[i]]+w[i]==f[i][j])
{
cout<<i<<" ";
j-=v[i];
}
return 0;
}