贪心策略:最优装载问题

趣学算法一:贪心算法

加勒比海盗船——最优装载问题

在北美洲东南部,有一片神秘的海域,那里碧海蓝天、阳光明媚,这正是传说中海盗最活跃的加勒比海。17世纪时,这里更是欧洲大陆的商旅舰队到达美洲的必经之地,所以当时的海盗活动非常猖獗,海盗不仅攻击过往商人,甚至攻击英国皇家舰…
有一天,海盗们截获了一艘装满各种各样古董的货船,每一件古董都价值连城,一旦打碎就失去了它的价值。虽然海盗船足够大,但载重量为C,每件古董的重量为wi,海盗们应该如何把尽可能多数量的宝贝装上海盗船呢?

题目要求尽可能多数量的宝贝,且“每件物品都价值连城”,那就说明,只要依次选择重量最小的古董,直到不能再装为止,就是最优的策略,因此,循环输入、一次排序算法,加上一个比较求解的循环就可以完成
因此,按照这个思路,第一版的代码很快就完成了:

#include
#include
const int N = 1000005;
using namespace std;
double w[N];
int main()
{
     
	double c; //定义载重量为double类型
	int n;    //定义古董数量为int类型
	cin >> c >> n;
	for (int i = 0; i < n; i++)
	{
     
		cin >> w[i];  //循环输入每个古董的质量
	}
	sort(w, w + n);  //升序排序
	double tmp = 0.0;   //已装载的古董的总质量
	int ans = 0;        //已装载的古董的总数量
	for (int i = 0; i < n; i++)
	{
     
		tmp += w[i];
		if (tmp <= c) //通过循环判断是否已经达到载重量
		    ans++;
		else
			break;
	}
	cout << ans << endl;
	return 0;
}

经过编译,没有任何问题
贪心策略:最优装载问题_第1张图片
那么我们来分析一下初版代码的时间复杂度吧。首先,循环语句的时间复杂度是O(n),sort函数的时间复杂度O(nlogn),因此总的时间复杂度为O(n+nlogn);代码的空间复杂度为O(1),没有什么问题。
在这组测试数据中,我们是并没有装满全部的载重量的,那为什么这个是最优装载呢?原因就是“每件宝物都价值连城”,宝物没有价值权重,并不需要为了装满而选择大的,因此选择质量最小的一定没有什么问题。因此,算法看来好像没有什么优化的途径了。
我们来看:
在寻找最优解时我们的判断语句是这样的

for (int i = 0; i < n; i++)
	{
     
		tmp += w[i];
		if (tmp <= c)
		    ans++;
		else
			break;
	}

那如果我们把循环结构改成如下这种格式呢?

for (int i = 0; i < n; i++)
	{
     
		tmp += w[i];
		if (tmp >= c)
		{
     
			if (tmp == c)
				ans = i + 1;
			else
				ans = i;
			break;
		}
	}

增加一个判断条件,判断了如果刚好放满后的情况。原版算法中,如果刚好放慢则需要再执行一次循环体、判断语句然后再跳出。而下面的算法中代码的思想则是如果刚好放满,则这件宝物可以放,否则就不能放,跳出循环体。因此在刚好可以放满的情况下就可以少执行一次循环,降低了算法的时间复杂度;且ans变量初始化时可设置为任意值,并不需要从0开始随着循环的进行而一次次的做自增运算。在做类似i++的运算时,会产生临时对象,而临时变量是需要占用内存空间的,因此,下面的算法会更合理一些。
我们再来看:
贪心策略:最优装载问题_第2张图片
在输入的数据非法时,程序并不能很好的报错,这里我们设置五个宝物的重量分别为1,1,1,1,-1;对于很明显的一个错误答案,程序还是勤勤恳恳的做求和运算,并最后得出可以装入3个宝物的错误答案。
程序的健壮性,其中一点就是指在面对错误的数据时程序可以运用错误处理机制来排查错误,以避免产生错误的输出结果。因此,我们加上错误处理机制,在输入的数字有错时及时报错即可。

你可能感兴趣的:(c++,贪心算法)