贪心算法
贪心算法总是选择当前看起来最优的选择(局部最优解),希望得到的结果是一个整体最优解。
但是,并非总是选择局部最优解就能够得到整体最优解,这一点需要在问题具有贪心选择性和优化子结构时才成立。
贪心选择性
- 贪心选择性:第一次做出贪心选择是正确的。
优化子结构
- 问题具有优化子结构性质:第一次做完贪心选择后,得到一个与原问题定义相同(但输入不同的)的子问题。
贪心算法的基本要素
贪心选择性质
-
贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
-
贪心算法通常以自顶向下的方式进行,一迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。动态规划通常以自底向上的方式解决子问题。
最优子结构性质
- 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
4. 基本思路:
- 建立数学模型来描述问题。
- 把求解的问题分成若干个子问题。
- 对每一子问题求解,得到子问题的局部最优解。
- 把子问题的解局部最优解合成原来解问题的一个解。
活动安排问题定义
定义(活动)
设有n个活动的集合 E={1,2,…,n}
,其中每个活动都要求使用同一资源,而在同一时间内只有一个活动能使用这个资源。
定义(相容活动)
- 若区间
[start_i, finish_i)
与区间[start_j, finish_j)
不相交,则称活动i与活动j是相容的。也就是说,当start_i >= finish_j
或start_j >= finish_i
时,活动i与活动j相容。
活动安排问题的贪心算法思路
- 将各个活动按照活动结束时间
finish_i
排序,且finish_1 <= finish_2 <= finish_3 …
- 选择活动1,要求该活动的结束时间最早。
- 从2开始按顺序比较各个活动,选择第一个与活动1相容的活动i。
- 从i+1开始按顺序考察各个活动,选择第一个与活动i相容的活动j。
每次选择与现有活动相容的结束时间最早的活动。
举例详解算法过程
现在给出下列活动,其中 s
表示开始时间,f
表示结束时间。
1.根据算法,我们首选选择结束时间最早的活动作为第一个活动,第一个活动为:1
2.然后选择开始时间大于第一个活动的结束时间的活动,而且该活动的结束时间要求最早。显然是活动4;
3.同理,寻找开始时间晚于活动4的结束时间且结束时间最早的活动,一直到到无活动可选为止。
代码示例
#include<stdio.h>
#include<stdlib.h>
#define N 100
typedef struct {
int begin;
int end;
int flag;//标记是否被安排
}active;
int main()
{
int num;//活动数量;
active act[N];
printf("请输入活动数量");
scanf("%d",&num);
printf("请输入活动的开始时间");
for(int i=0;i<num;i++)
{
scanf("%d",&act[i].begin);
}
printf("请输入活动的结束时间");
for(int j=0;j<num;j++)
{
scanf("%d",&act[j].end);
}
sort(act,num);
greedy(act,num);
}
void greedy(active a[],int num)
{
int current=0;//当前活动下标
for(int i=0;i<num;i++)
{
a[i].flag = 0;//初始所有活动都未被安排
}
a[0].flag = 1;//排序后的第一个活动被安排
/**
//后面的活动开始的时间晚于当前活动开始的时间则相容
//而当前活动初始下标为0,故后面的活动i下标要从1开始
**/
for(int i=1;i<num;i++)
{
if(a[i].begin >= a[current].end)
{
a[i].flag = 1;//相容,做标记
current=i;//当前活动变为i
}
}
//输出
printf("\n\n");
printf("按结束时间排序后的活动序号中,以下活动将被安排:\n\n");
int count=0;
for(int i=0;i<num;i++)
{
if(a[i].flag==1){
count++;//被安排的活动数量;
printf("活动%d :开始时间 结束时间:%d,%d\n",i,a[i].begin,a[i].end);
printf("\n");
}
}
printf("总计%d个活动被安排\n",count);
}
//按照结束时间非降序排序
void sort(active a[],int n)
{
for (int i=0;i<n;i++)
{
for(int j=0;j<n-i-1;j++)
{
if(a[j].end>a[j+1].end){
active temp;
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
}