greedy算法策略高效求解分数背包问题

本文介绍了如何利用贪婪算法和归并排序高效地解决分数背包问题。关键在于对物品按性价比排序,通过归并排序实现快速排序,确保在装包过程中始终选择性价比最高的物品。同时,文章讨论了如何在排序后保持物品价值和重量的对应关系,以及贪婪算法与分治法在解决问题上的区别和结合应用。
摘要由CSDN通过智能技术生成

          通过greedy可以求解分数背包问题,其中编程的关键点是对物品按照其性价比进行排序,所以本文采用前面学到的高效的排序方法:基于分治思想的归并排序法进行排序。这样实现了高效的求解分数背包问题,具体求解思维过程如下:

          (1)通过greedy可以求解分数背包问题,其中编程的关键点是对物品按照其性价比进行排序,所以本文采用前面学到的高效的排序方法:基于分治思想的归并排序法进行排序。这样实现了高效的求解分数背包问题,具体求解过程如下:分数背包问题相比于我们知道的0-1背包问题,最大的区别是0-1背包有一个硬性条件:被装载到背包里面的物品不能切分,只能整个物品装入;而分数背包则可以将物品切分成小块塞满整个书包。所以0-1背包问题和分数背包问题在编程实现上没有本质差别,只需要对最后一个待装入的物品按照剩余的背包空间进行比例划分装入。

          (2)为什么背包问题能够采用greedy算法来求解呢?一个重要思想是选择当前可选所有结果中最好的结果。在背包问题中当前所选结果中最好的结果是性价比最好的物品,即v/w最大的物品。那为什么依据的是v/w最大的呢?我们可以设定当前的背包只有一个单位空间,那么想要获取最大的价值(这里背包可以切割划分),那么只有一个显然的满足条件是选取价值比最大的,因为背包的单位空间已定,则物品的单位空间的价值越大,则显然我们放入的物品的价值就最大。推广到整个背包空间,背包的空间是已定的,则每次在放入背包的物品中,在同等空间下,价值越大,则当前得到的总价值就越大。而同等空间下,价值越大不就是性价比最大吗,即物品v/w最大。因此,按照这种思想,可每次将剩余的v/w最大的物品塞入包中,因为这样是在当前同等空间中去最大值,所以能够理解这种做法的正确性。

          (3)那么,如何编程实现了?既然每次都是按照当前剩余物品中性价比最大的进包,那么首先对所有物品进行性价比的计算,然后从大到小进行排序,最后用一个for循环进行装包,即设定一个变量累加。整个算法实现的关键是如何对所有物品按照性价比进行排序?最先想到的是采用分治法中的归并排序进行高效排序,其中也遇到了一个棘手的问题,就是如果仅对物品的性价比排序,即先计算v/w,再push到vector<double>nums 中,这样虽然能够对所有的物品的性价比进行排序,但是排完序之后,无法对应到原来物品的价值v 和重量w,因为同等价值的物品,其对应的v 和w不同。那么怎么办了,这时想到了一个数据结构可以实现数据标签和值的对应:vector<map<double, double>::iterator> &nums 和vector<map<double, double>> numC; numC是很多map的集合,而一个map就可以装N个元素,所以对于N个物品,只需要一个map即可。如何操作元素,可以同过该map的迭代器进行操作,所以最后通过vector<map::iterator>实现,本质上是对push在vector中指向元素的指针进行操作,所以在对迭代器进行排序和计算性价比后不会改变对应指针指定的物品。

      整体的框架主函数为:

double GreedyStrategy::fractionalKnapsack(map<double, double> &commodities, double sumCapacity)
{
	map<double, double>::iterator mapIter = commodities.begin();                          //方法:先对货物按照价值比进行大小排序,// map<value,weight>
	vector<map<double, double>::iterator> numMapPtr;
	while (mapIter!=commodities.end())
	{
		numMapPtr.push_back(mapIter);
		mapIter++;
	}

	mergeSort(numMapPtr,0,numMapPtr.size()-1);                                             //numMapPtr 已按从大到小排好序     //界定范围为首元素和最后一个元素
	

	//以下是装包操作,其实就是通过一个变量累加物品的价值就可实现装包的操作,因为最后要的就是一个最大化价值;
	//物品one by one装包的编程实现就是通过一个循环条件:当前背包中物品累加的总重量与背包本身的装载最大重量的比较,若当前背包中物品的总重量小于其额度限制重量,则指针+1,遍历已排序的numMapPtr[i]下一个指向物品的指针进行累加操作。 
	double maxValue=0;
	double maxWeight = 0;
	int i = 0;
	while (maxWeight<sumCapacity)
	{
		if (maxWeight + numMapPtr[i]->second > sumCapacity)                               //取小数
		{
			double rateValue = (sumCapacity - maxWeight)*(numMapPtr[i]->first / numMapPtr[i]->second);
			maxValue += rateValue;
			break;
		}
		else
		{
			maxValue = maxValue + numMapPtr[i]->first;                                //注:以重量为比较,second 表示重量
			maxWeight = maxWeight + numMapPtr[i]->second;
		}
		i++;
	}
	
	return maxValue;                                                                //最后返回最大的value, 重量肯定是装满,因为价值各不相同,所以有最大价值
}

       实现性价比排序算法如下:

void GreedyStrategy::mergeSort(vector<map<double, double>::iterator> &nums, int low, int high)
{
	if (low < high)
	{
		int mid = (low + high) / 2;
		mergeSort(nums, low, mid);
		mergeSort(nums, mid + 1, high);
		conquer(nums,low, mid,  high);
	}
}

void GreedyStrategy::conquer(vector<map<double, double>::iterator>& num, int low, int mid, int high)
{
	int i, j;
	i = low;
	j = mid + 1;
	double priceA, priceB;
	vector<map<double, double>::iterator> numC;

	vector<map<double, double>> numD;
	
	while (i<=mid&&j<=high)              
	{                 
		priceA = num[i]->first / (num[i]->second*1.0);
		priceB = num[j]->first / (num[j]->second*1.0);
		if (priceA < priceB)                               //这里的比较转换为v/w的比较,然后将	numMapPtr.push_back(mapIter); mapIter++;	  // 分治法的归并排序,不必直接进行数的排序,可以是任意其他类型如指针,价值等的排序,开放应用。数据
		{
			numC.push_back(num[j]);
			j++;
		}
		else
		{
			numC.push_back(num[i]);
			i++;
		}
	}

	while (i<=mid)
	{
		numC.push_back(num[i]);
		i++;
	}

	while (j <= high)
	{
		numC.push_back(num[j]);
		j++;
	}
	
	for (int i = low,k = 0; i <= high; i++,k++)
	{
		num[i] = numC[k];
	}
}

              总结:greedy方法实际上是一种求解问题的策略,即对于某一问题,我们可以考虑采用greedy思想来求解其目前最好的解,因此该方法与之前的分治法不同的是,分治法是对问题(该问题可暴力求解解)进行高效的求解,分治法重点在时间效率上;而greedy则是针对某些陌生问题,最开始并不知道如何去求解它,因此可采用greedy进行近似最好的预期结果的求解,因此greedy方法主要在于如何去求解问题,而不是如何提高效率上。而分治法往往针对的是一些可以暴力求解的问题,我们采用分治思想可提高效率。

        但是,二者又可以结合起来求解问题,在greedy能够分析、求解问题的时,对于其求解过程中出现数排序、查找等操作就可采用分治思想来提高过程中的计算效率,从而达到既能求解问题,又能高效求解,如上文的分数背包问题。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值