要求背包的一个具体方案,就是求他每一步的决策,然后输出所有决策为1(取)的选择
要输出字典序最小的方案
由于要输出字典序最小的方案,所以最终方案能选择1,就尽量选择,能选择2,就尽量选择...
所以枚举应该是从后往前递推,如果字典序越小的物品能够选择就尽可能的选择
for(int i=n;i>=1;i--) //从后往前递推
for(int j=1;j<=m;j++)
{
dp[i][j]=dp[i+1][j];
if(j>=v[i])
dp[i][j]=max(dp[i+1][j-v[i]]+w[i],dp[i][j]);
}
int k=m;
for(int i=1;i<=n;i++)//从字典序小的开始枚举,字典序小的物品能选就尽量选
if(k>=v[i]&&dp[i][k]==dp[i+1][k-v[i]]+w[i])
{
k-=v[i];//每次减掉当前物品的容积
cout<<i<<' ';
}
注意:
如果运用01背包的方法,开始时
求背包最大值从前往后枚举,最后输出从后往前选择,不能得到字典序最小的方案,因为如果这样的话,是变成从n开始,能选择n就尽量选择...和我们字典序最小的贪心策略相悖
反而得到字典序最大的
ac代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1005;
int dp[N][N];
int v[N],w[N];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(int i=n;i>=1;i--)
for(int j=1;j<=m;j++)
{
dp[i][j]=dp[i+1][j];
if(j>=v[i])
dp[i][j]=max(dp[i][j],dp[i+1][j-v[i]]+w[i]);
}
int k=m;
for(int i=1;i<=n;i++)
if(k>=v[i]&&dp[i][k]==dp[i+1][k-v[i]]+w[i])
{
cout<<i<<' ';
k-=v[i];
}
return 0;
}
正推的版本,需要reverse
没有reverse就是字典序最大的最优方案,以下是字典序最大的方案,从后往前字典序大的尽可能选
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N=1005;
int dp[N][N];
int v[N],w[N];
vector<int> res;
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
dp[i][j]=dp[i-1][j];
if(j>=v[i]) dp[i][j]=max(dp[i-1][j-v[i]]+w[i],dp[i][j]);
}
int k=m;
for(int i=n;i>=1;i--)
if(k>=v[i]&&dp[i][k]==dp[i-1][k-v[i]]+w[i])
res.push_back(i),k-=v[i];
reverse(res.begin(),res.end());
for(auto t:res)
cout<<t<<' ';
return 0;
}