您的位置:首页 > 其它

利用贪心算法解背包问题

2013-10-04 19:45 411 查看
一、贪心算法介绍

贪心算法总是作出在当前看来是最好的选择,即贪心算法并不从整体最优解上加以考虑,它所作出的选择只是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广的许多问题它能产生整体最优解。如图的单源最短路径、最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,但其最终结果却是最优解的很好近似解。

贪心算法求解的问题一般具有两个重要性质:贪心选择性质和最优子结构性质。所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优解的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

贪心算法与动态规划算法的差异,将通过下面的利用贪心算法求解背包问题来说明。

二、背包问题

【问题描述】:先来看看0-1背包问题的描述:给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包容量为c。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大。在选择装入背包的物品时,对每种物品i只有两种选择,即装入背包或不装入背包。不能将物品i装入背包多次,也不能只装入部分的物品i。背包问题与0-1背包问题类似,所不同的是在选择物品i装入背包时,可以选择物品i的一部分,而不一定要全部装入背包,1<=i<=n。

【算法分析】:这两类问题都具有最优子结构性质。对于0-1背包问题,设A是能装入容量为c的背包的具有最大价值的物品集合,则Aj=A-{j}是n-1个物品1,2,...,j-1,j+1,...,n可装入容量为c-wj的背包的具有最大价值的物品集合。对于背包问题,类似地,若它的一个最优解包含物品j,则从该最优解中拿出所含的物品j的那部分重量w,剩余的将是n-1个原重物品1,2,...,j-1,j+1,...,n及重为wj-w的物品j中可装入容量为c-w的背包且具有最大价值的物品。

虽然这两个问题极为相似,但背包问题可以用贪心算法求解,而0-1背包问题却不能用贪心算法求解。用贪心算法求解背包问题的步骤是,首先计算每种物品单位重量的价值vi/wi;然后,依贪心选择策略,将尽可能多的单位重量价值最高的物品装入背包。若将这种物品全部装入背包后,背包内的物品总量未超过c,则选择单位重量价值次高的物品并尽可能多地装入背包。依此策略一直进行下去,直到背包装满为止。

【算法实现】Knapsack算法的C++实现如下:

(其中背包信息从文件中读取,点击进入该文件下载界面)

001
#include
"iostream"
002
003
#define
n 50   //物品的数量
004
005
//物体重量、收益、背包容量
006
int
weight
,
profit
, contain;
007
//物体的单位重量价值
008
float
pervalues
,
x
;
009
010
//从文件中读取背包信息
011
int
read_infor()
012
{
013
FILE
*fp;
014
int
i;
015
if
((fp=
fopen
(
"knapsack.txt"
,
"r"
))==NULL)
016
{
017
printf
(
"The
file is not found!"
);
018
return
0;
019
}
020
//读取物体收益信息
021
for
(i
= 0;i < n;i++)
022
{
023
fscanf
(fp,
"%d"
,
&profit[i]);
024
}
025
//读取物体重量信息,计算物体的单位重量价值
026
for
(i
= 0;i < n;i++)
027
{
028
fscanf
(fp,
"%d"
,
&weight[i]);
029
pervalues[i]
= profit[i] / (
float
)weight[i];
030
}
031
//读取背包容量
032
fscanf
(fp,
"%d"
,
&contain);
033
fclose
(fp);
034
return
1;
035
}
036
037
void
sort()
038
{
039
int
i,
j, max, tmp;
040
float
ftmp;
041
for
(i
= 0;i < n;i++)
042
{
043
max
= i;
044
for
(j
= i + 1;j < n;j++)
045
{
046
if
(pervalues[max]
< pervalues[j])
047
{
048
max
= j;
049
}
050
}
051
ftmp
= pervalues[i];
052
pervalues[i]
= pervalues[max];
053
pervalues[max]
= ftmp;
054
055
tmp
= weight[i];
056
weight[i]
= weight[max];
057
weight[max]
= tmp;
058
059
tmp
= profit[i];
060
profit[i]
= profit[max];
061
profit[max]
= tmp;
062
}
063
}
064
065
void
Knapsack()
066
{
067
int
i,
c = contain;
068
for
(i
= 0;i < n;i++)
069
{
070
x[i]
= 0;
071
}
072
for
(i
= 0;i < n;i++)
073
{
074
if
(weight[i]
> c)
075
{
076
break
;
077
}
078
x[i]
= 1;
079
c
-= weight[i];
080
}
081
if
(i
< n)
082
{
083
x[i]
= c / (
float
)weight[i];
084
}
085
}
086
087
void
main()
088
{
089
int
i;
090
float
sumWeight
= 0, sumProfit = 0;
091
if
(read_infor())
092
{
093
sort();
094
Knapsack();
095
for
(i
= 0;i < n;i++)
096
{
097
sumWeight
+= weight[i] * x[i];
098
sumProfit
+= profit[i] * x[i];
099
}
100
printf
(
"The
maximum profit is: %f."
,
sumProfit);
101
printf
(
"\nThe
knapsack weight is %f.\n"
,
sumWeight);
102
}
103
scanf
(
"%d"
,
&i);
104
}
【算法复杂度分析】:算法Knapsack的主要计算时间在于将各种物品依其单位重量的价值从大到小排序。因此,算法的时间上界为O(nlogn)。

对于0-1背包问题,贪心选择之所以不能得到最优解是因为在这种情况下,它无法保证最终能将背包装满,部分闲置的背包空间使每千克背包空间的价值降低了。事实上,在考虑0-1背包问题时,应比较选择该物品和不选择该物品所导致的最终方案,然后再做出最好选择。由此可导出许多互相重叠的问题。这正是该问题可用动态规划算法求解的另一重要特征。动态规划算法的确可以有效解决0-1背包问题。

ref:http://blog.stevenwang.name/greedy-knapsack-problem-31004.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: