这个题目自己做得比较满意,至少从思路上来说,非常满意。因为我完全独立思考地把它转化成了一道01背包问题。
不过在做的过程当中也遇到了一个问题。就是01背包选择后,输出方案的时候发现以前一直以为是正确的方法,现在终于发现有漏洞了。
把问题抽象出一个简单一点的模型吧:
一个背包,背包的能承受的重量为上限为V;有N个物品,物品的重量分别为v1,v2...vN.
求最大可能地装满背包(让背包装的东西尽可能重)时,应该选择哪些物品。
解析:这是一个经典的01背包问题。直接利用滚动数组背包就行。然后反向搜索看应该选择那些物品。
经典的代码:
#include <iostream>
using namespace std;
int V;
int N;
int pack[200];
int a[200];
const int INFINITE = 1<<30;
int choose[200];
void ZeroOnePack( const int cost )
{
for(int v = V; v>=cost; --v)
if( pack[v-cost] != -INFINITE )
pack[v] = pack[v] < pack[v-cost] + cost ? pack[v-cost] + cost : pack[v];
}
void get_path(int Stat)
{
for(int i=N-1; i>=0; --i)
{
if( Stat-a[i]>=0 && pack[Stat-a[i]] + a[i] == pack[Stat] )
{
choose[i] = true;
Stat -= a[i];
}
}
}
int main()
{
freopen("in.txt","r",stdin);
scanf("%d %d",&V,&N);
for(int i=0; i<N; ++i)
scanf("%d",a+i);
//init
pack[0] = 0;
for(int i=1; i<=V; ++i)
pack[i] = -INFINITE;
for(int i=0; i<N; ++i)
ZeroOnePack( a[i] );
int Stat = V;
while( pack[Stat] < 0 ){
--Stat;
}
for(int i=0; i<N; ++i)
choose[i] = false;
get_path( Stat );
for(int i=0; i<N; ++i)
if( choose[i] )
printf(" %d",i+1);
printf("\n");
return 0;
}
考虑某组测试:
V = 100
N = 5
物品重量分别为:25 41 33 50 33
那么输出的选择会出现错误:
它会想装满99的重量,但是它会选择第3和第5号这两个物品。
所以教训是:
涉及到输出选择方案时:谨慎使用滚动数组,LRJ说得还是非常在理,不听老人言吃亏在眼前啊。