利用贪心算法解背包问题
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++实现如下:
(其中背包信息从文件中读取,点击进入该文件下载界面)
【算法复杂度分析】:算法Knapsack的主要计算时间在于将各种物品依其单位重量的价值从大到小排序。因此,算法的时间上界为O(nlogn)。
对于0-1背包问题,贪心选择之所以不能得到最优解是因为在这种情况下,它无法保证最终能将背包装满,部分闲置的背包空间使每千克背包空间的价值降低了。事实上,在考虑0-1背包问题时,应比较选择该物品和不选择该物品所导致的最终方案,然后再做出最好选择。由此可导出许多互相重叠的问题。这正是该问题可用动态规划算法求解的另一重要特征。动态规划算法的确可以有效解决0-1背包问题。
ref:http://blog.stevenwang.name/greedy-knapsack-problem-31004.html
贪心算法总是作出在当前看来是最好的选择,即贪心算法并不从整体最优解上加以考虑,它所作出的选择只是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广的许多问题它能产生整体最优解。如图的单源最短路径、最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,但其最终结果却是最优解的很好近似解。
贪心算法求解的问题一般具有两个重要性质:贪心选择性质和最优子结构性质。所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优解的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
贪心算法与动态规划算法的差异,将通过下面的利用贪心算法求解背包问题来说明。
二、背包问题
【问题描述】:先来看看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 | } |
对于0-1背包问题,贪心选择之所以不能得到最优解是因为在这种情况下,它无法保证最终能将背包装满,部分闲置的背包空间使每千克背包空间的价值降低了。事实上,在考虑0-1背包问题时,应比较选择该物品和不选择该物品所导致的最终方案,然后再做出最好选择。由此可导出许多互相重叠的问题。这正是该问题可用动态规划算法求解的另一重要特征。动态规划算法的确可以有效解决0-1背包问题。
ref:http://blog.stevenwang.name/greedy-knapsack-problem-31004.html
相关文章推荐
- 【算法】贪心算法之背包与装箱问题
- 贪心算法解决部分背包问题(C语言,ruby)
- 贪心算法求解背包问题
- 算法学习:贪心解部分背包问题
- 贪心算法----背包问题
- 数据结构与算法学习之路:背包问题的贪心算法和动态规划算法
- 贪心算法 - 背包问题
- 【算法导论实验5】贪心-活动安排问题与背包问题
- 九种背包问题,让你永恒拥有背包算法(贪心)
- 贪心算法解决背包问题
- 贪心算法运用于(0/1)背包问题
- 十大算法之一贪心—解背包问题-C#代码
- 背包问题的贪心算法
- 贪心算法练习题:部分背包问题
- 【算法】贪心策略实现部分背包问题
- 贪心算法-4.2背包问题
- 贪心算法之背包问题
- 贪心算法之背包问题
- 贪心算法 安排活动的问题 最大限度的利用有限资源
- 贪心算法-背包问题2