贪心算法解决背包问题有几种策略:
(i)一种贪婪准则为:从剩余的物品中,选出可以装入背包的价值最大的物品,利用这种规则,价值最大的物品首先被装入(假设有足够容量),然后是下一个价值最大的物品,如此继续下去。这种策略不能保证得到最优解。例如,考虑n=2,
w=[100,10,10], p =[20,15,15], c = 105。当利用价值贪婪准则时,获得的解为x= [ 1 , 0 , 0
],这种方案的总价值为2 0。而最优解为[ 0 , 1 , 1 ],其总价值为3 0。
(ii)另一种方案是重量贪婪准则是:从剩下的物品中选择可装入背包的重量最小的物品。虽然这种规则对于前面的例子能产生最优解,但在一般情况下则不一定能得到最优解。考虑n=
2 ,w=[10,20], p=[5,100], c= 2 5。当利用重量贪婪策略时,获得的解为x =[1,0], 比最优解[ 0 , 1
]要差。
(iii)还有一种贪婪准则,就是我们教材上提到的,认为,每一项计算yi=vi/si,即该项值和大小的比,再按比值的降序来排序,从第一项开始装背包,然后是第二项,依次类推,尽可能的多放,直到装满背包。
有的参考资料也称为价值密度pi/wi贪婪算法。这种策略也不能保证得到最优解。利用此策略试解n= 3 ,w=[20,15,15],
p=[40,25,25], c=30 时的最优解。虽然按pi /wi
非递(增)减的次序装入物品不能保证得到最优解,但它是一个直觉上近似的解。
而且这是解决普通背包问题的最优解,因为在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包,1≤i≤n。
即不论何种贪心算法,都不能保证得到最优解。
下面考虑使用动态规划的方法求解:
开始时考虑递归求解,但超时了,看来是递归的时间太长了。
#include<stdio.h>
#include<algorithm>
using namespace std;
struct thing
{
int t;
int value;
}ts[101];
int get_max(int i,int j)
{
return i>j?i:j;
}
int get_result(int M,int T)
{
if(T==0||M<=0)
return 0;
int result_tmp = get_max(get_result(M-1,T)+ts[M].value,get_result(M-1,T-ts[M].t));
return result_tmp;
}
int main()
{
int T,M;
while(scanf("%d %d",&T,&M)!=EOF)
{
for(int i=0;i<M;i++)
{
scanf("%d %d",&ts[i].t,&ts[i].value);
}
int result = get_result(M,T);
printf("%d\n",result);
}
return 0;
}
然后换成迭代,注意空间为负值时要将值设置为前一个物体未放入时的值:
#include<stdio.h>
#include<algorithm>
using namespace std;
struct thing
{
int t;
int value;
}ts[101];
int result[101][1001];
int get_max(int i,int j)
{
return i>j?i:j;
}
int main()
{
int T,M;
while(scanf("%d %d",&T,&M)!=EOF)
{
for(int i=1;i<=M;i++)
{
scanf("%d %d",&ts[i].t,&ts[i].value);
}
for(int i=0;i<=1000;i++)
result[0][i]=0;
for(int i=1;i<=M;i++)
{
for(int j=1;j<=1000;j++)
{
if(j-ts[i].t>=0)
{
result[i][j]=get_max(result[i-1][j-ts[i].t]+ts[i].value,result[i-1][j]);
}
else
result[i][j]=result[i-1][j];
}
}
printf("%d\n",result[M][T]);
}
return 0;
}