利用贪心算法解背包问题

一、贪心算法介绍

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

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

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

二、背包问题

【问题描述】:先来看看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[n], profit[n], contain;
007 //物体的单位重量价值
008 float pervalues[n], x[n];
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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值