今天做了一道01背包加最小字典序输出的问题,就稍微总结一下然后说些理解,如果有错还望大神指出。
题目地址戳这里
路径的存储有两种方法,一种是用path二维数组记录一下路径,另一种是直接根据dp数组逆着找回去.
第一种方法:优化后和没优化的dp数组都可以使用
第二种方法:只能用于没有优化后的dp数组,一维存的信息不够.(因为一维边存边丢.)
至于要输出字典序最小的方案,采取贪心的思想,从第一个物品开始判断取还是不取,那么此时在dp的时候我们也要从后往前看每个物品。而要输出字典序最大的方案那么就直接从后面向前面看就可以了
for(int i=1;i<=n;i++) //n个物品,用结构体Input存储
{
for(int j=m;j>=input[i].cost;j--)
{
dp[j]=dp[j];
if(dp[j-input[i].cost]+input[i].v>dp[j])
{
path[i][j]=1; //path 记录路径
dp[j]=dp[j-input[i].cost]+input[i].v;
}
}
}
int i=n,j=m;
while(i&&j)
{
if(path[i][j]==1)
{
printf("%d\n",i); //拿了第i个物品,输出方案
j-=input[i].cost;
}
i--;
}
方法二
for(int i=1;i<=n;i++) //n个物品,用结构体Input存储
{
for(int j=input[i].cost;j<=m;j++) //m是背包容量
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-input[i].cost]+input[i].v);
}
}
int i=n,j=m;
while(i&&j)
{
if(dp[i][j]==dp[i-1][j-input[i].cost]+input[i])
{
printf("%d\n",i);
j-=input[i].cost;
}
i--;
}
//如果要输出最小字典序
for(int i=n;i>=1;i--) //从最后一个物品开始拿,此时dp[i][j]表示后n-i+1个物品中体积为j时的最大价值
{
for(int j=V;j>=0;j--)
{
if(j>=v[i])
dp[i][j]=max(dp[i+1][j],dp[i+1][j-v[i]]+w[i]); //v[i]第i 个物品的体积,w[i]第i个物品的价值
else
dp[i][j]=dp[i+1][j];
}
}
//输出路径的方案
int i=1,j=V;
while(i<=n&&j>0) //从第一个物品开始看拿没拿
{
if(j>=v[i]&&dp[i][j]==dp[i+1][j-v[i]]+w[i]) {
j-=v[i];
cout<<i<<' ';
} //拿了就输出,再把体积减去,值得注意一下的是边界条件j>=v[i],如果没有这个条件的话就会出现数组下标越界答案出错。
i++; //判断下一个物品
}