Greedy——活动选择问题

本文介绍如何通过贪心算法解决活动选择问题,即在一系列活动中找出最大的互不冲突的活动子集。文章提供了算法的伪代码及C++实现,并解释了贪心算法的关键特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

活动选择问题:就是给定一组活动的开始时间和结束时间,然后他们都需要使用到一个资源,这个资源每次只有一个活动可以用,要求求出一个最大的相互兼容的活动子集。

首先定义了一个集合Sij = {ak S :fi sk < fk sj} , 其中S就是所有活动的集合,fi是活动ai的完成时间si是活动ai的开始时间。

这道题如果是用DP来解的话,就需要找到最优解的递归方程(其中c[i,j]是集合Sij中的最大兼容活动的个数):

然后根据DP自底向上的解答方法进行求解,过程和矩阵连乘的差不多。

然而,根据下面的定理,我们可以用贪心的策略来解答这道题目:


这样子就将DP的两个子问题变成了只有一个子问题,而且由于将结束时间已升序排序,所以要求出fm也是很简单的。

接下来的工资就变得很简单了,看下面的伪代码和图片即可

RECURSIVE-ACTIVITY-SELECTOR(s, f, i, j)
1 m ← i + 1
2 while m  < j and sm < fi ▹ Find the first activity in Sij.
3     do m ← m + 1
4 if m < j
5    then return {am} ∪ RECURSIVE-ACTIVITY-SELECTOR(s, f, m, j)
6    else return Ø
下面的图片是利用上面的递归算法求解的过程



下面是代码,将递归改为迭代来实现。

#include <iostream>
#include <algorithm>
using namespace std;

struct Activity
{
	int s; //start time
	int f; //finish time
};

bool cmp(Activity A,Activity B)
{
	return A.f<B.f;
}

int main()
{
	Activity act[100];
	Activity temp[100];
	int n;
	cout<<"一共有多少个活动:";
	cin>>n;
	cout<<"输入每个活动的开始时间和结束时间:"<<endl;
	for(int i=0;i<n;i++)
		cin>>act[i].s>>act[i].f;
	sort(act,act+n,cmp);

	int k=0;
	temp[k]=act[0];
	for(i=1;i<n;i++)
	{
		while(act[i].s >= temp[k].f)
		{
			k++;
			temp[k]=act[i];
		}
	}
	cout<<"最后选择的兼容的活动的开始时间和结束时间如下:"<<endl;
	for(i=0;i<=k;i++)
		cout<<temp[i].s<<"  "<<temp[i].f<<endl;
	return 0;
}

运行结果:



运用贪心解决的问题具有两个特点:贪心选择性质和最优子结构

贪心选择性质:

一个全局最优解可以通过局部最优选择来达到,就是说,当我们做选择的时候,我们只考虑对当前问题最佳的选择而不考虑子问题的结果。这个和DP是不一样的,在DP中,每一步都要做出选择,但是这些选择依赖于子问题的解。但是在贪心算法中,所做的当前选择可能会依赖于已经做出的所有选择,但不依赖于有待做出的选择或者是子问题的解,所以DP是自底向上的,而贪心是自顶向下的。

最优子结构:

对于一个问题来说,如果它的一个最优解包含了其子问题的最优解,则说该问题具有最优子结构。

在贪心中,假设一个贪心选择后得到一个子问题,真正要做的是证明这个子问题的最优解与所做的贪心选择合并后,的确可以得到原问题的一个最优解。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值