趣学算法--贪心算法

目录

一、贪心算法的性质

        1.1贪心选择

        1.2最优子结

二、贪心算法使用策略

        2.1贪心策略

        2.2局部最优解

        2.3全局最优解

三、贪心算法的应用

        3.1最简单的最优装载问题

        3.2简单的会议安排问题

        3.2.1算法设计:

        3.2.2算法复杂度分析:


一、贪心算法的性质

        1.1贪心选择

        贪心选择性质是指原问题的整体最优解可以通过一系列局部最优的选择得到。将原问题变为一个相似的但规模更小的子问题,而后的每一步都是当前最佳的选择。这种选择依赖于已做出的选择,但不依赖于未做出的选择。

        1.2最优子结

        如果问题的最优解包含其子问题的最优解,称此问题具有最优子结构性质。问题的最优子结构性质是该问题是否可用贪心算法求解的关键。例如原问题S={a1,a2,…,ai,…,an},通过贪心选择选出一个当前最优解{ai}之后,转化为求解子问题S−{ai},如果原问题的最优解包含子问题的最优解,则该问题满足最优子结构性质。

                

二、贪心算法使用策略

        2.1贪心策略

        首先确定贪心策略,选择当前最好的一个方案。例如,挑选苹果,如果要求个大的是最好的,那每次都从苹果堆中拿一个最大的,作为局部最优解,贪心策略就是选择当前最大的苹果;如果要求最红的苹果是最好的,那每次都从苹果堆中拿一个最红的,贪心策略就是选择当前最红的苹果。 因此根据求解目标不同,贪心策略也会不同。

        2.2局部最优解

        根据贪心策略,一步一步地得到局部最优解。例如,第一次选一个最大的苹果放起来,记为a1,第二次再从剩下的苹果堆中选择一个最大的苹果放起来,记为a2,以此类推。

        2.3全局最优解

        把所有的局部最优解合成为原来问题的一个最优解(a1,a2,…)。

三、贪心算法的应用

        3.1最简单的最优装载问题

        假如有一艘船承重量为C,有n件货物,每件货物重量为Wn,要求装载的货物尽可能多,应该怎么分配?

        贪心选择策略:优先装载最小重量的货物。

        从局部最优达到全局最优,从而产生最优装载问题的最优解。

	void Run()
	{
		int weight[10] = { 2,6,11,65,35,20,6,23,29,30 };
		std::sort(weight, weight + 10);

		int c = 100;
		int num = 0;
		int tempC = 0;
		for (int i = 0; i < 10; ++i)
		{
			tempC += weight[i];
			num++;
			if (tempC >= c)
				break;
		}
		printf("最多可以装载%d件货物\n", num);
	}

         时间复杂度:首先需要按古董重量排序,调用sort函数,其平均时间复杂度为O(nlogn),输入和贪心策略求解的for语句时间复杂度均为O(n),因此时间复杂度为O(nlogn)。

        空间复杂度:程序中变量num、tempC等占用了一些辅助空间,这些辅助空间都是常数阶的,因此空间复杂度为O(1)。

        3.2简单的会议安排问题

        会议安排的目的是能在有限的时间内召开更多的会议(任何两个会议不能同时进行)。在会议安排中,每个会议i都有起始时间bi和结束时间ei,且bi<ei,即一个会议进行的时间为半开区间[bi,ei)。如果[bi,ei)与[bj,ej)均在“有限的时间内”,且不相交,则称会议i与会议j相容的。

        

         

        会议数最多,需要选择最多的不相交时间段。可以尝试以下贪心策略

        (1)最早开始时间且与已安排的会议相容的会议。

        (2)持续时间最短且与已安排的会议相容的会议。

        (3)最早结束时间且与已安排的会议相容的会议。

        最好选择那些开始时间要早,而且持续时间短的会议,即最早开始时间+持续时间最短,就是最早结束时间。

        贪心策略:

        每次从剩下的会议中选择具有最早结束时间且与已安排的会议相容的会议安排。

         3.2.1算法设计:

          1)初始化:将n个会议的开始时间、结束时间存放在结构体数组中然后按结束时间非递减排序,结束时间相等时,按开始时间非递增;
        (2)根据贪心策略选择第一个具有最早结束时间的会议,用last记录刚选中会议的结束时间;
        (3)选择第一个会议之后,依次从剩下未安排的会议中选择,如果会议i开始时间大于等于last,那么会议i可以安排,更新last为刚选中会议的结束时间;否则,舍弃会议i,检查下一个会议是否可以安排。

struct Meeting
	{
		int meetNum;
		int beg;
		int end;
	};

    //比较函数,按结束时间增大排序(非递减),如果结束时间相同,则会议时间越短越靠前排(非递增排序)
	bool CMP(Meeting m1, Meeting m2)
	{
		if (m1.end == m2.end)
			return m1.beg > m2.beg;

		return m1.end < m2.end;
	}

	void Run()
	{
		Meeting meet[10] = {
			{1,8,10},
			{2,9,11},
			{3,10,15},
			{4,11,14},
			{5,13,16},
			{6,14,17},
			{7,15,17},
			{8,17,18},
			{9,18,20},
			{10,16,19}
		};

		std::sort(meet, meet + 10, CMP);
		cout << "按结束时间早结束排序后的会议" << endl;
		cout << "会议号 开始时间 结束时间" << endl;
		for (int i = 0; i < 10; ++i)
		{
			cout << meet[i].meetNum << "  " << meet[i].beg << "  " << meet[i].end << endl;
		}

		int lastEnd = 0;
		int num = 0;
		for (int i = 0; i < 10; ++i)
		{
			if (lastEnd <= meet[i].beg)
			{
				printf("选择第%d个会议\n", meet[i].meetNum);
				lastEnd = meet[i].end;
				num++;
			}
		}
		printf("最多安排%d个会议\n", num);
	}

        3.2.2算法复杂度分析:

        (1)时间复杂度:输入n个会议,共执行n次。sort排序函数的平均时间复杂度为O(nlogn)。选择会议,需要扫描整个会议列表,时间复杂度为O(n),总时间复杂度为O(n+nlogn)= O(nlogn)。 
        (2)空间复杂度:辅助空间有num、lastEnd等变量,则该程序空间复杂度为常数阶,即O(1)。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值