贪心算法-活动安排问题

贪心算法

贪心算法总是选择当前看起来最优的选择(局部最优解),希望得到的结果是一个整体最优解。

但是,并非总是选择局部最优解就能够得到整体最优解,这一点需要在问题具有贪心选择性优化子结构时才成立。

贪心选择性

  • 贪心选择性:第一次做出贪心选择是正确的。

优化子结构

  • 问题具有优化子结构性质:第一次做完贪心选择后,得到一个与原问题定义相同(但输入不同的)的子问题。

贪心算法的基本要素

贪心选择性质

  1. 贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。

  2. 贪心算法通常以自顶向下的方式进行,一迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。动态规划通常以自底向上的方式解决子问题。

最优子结构性质

  • 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

4. 基本思路:

  • 建立数学模型来描述问题。
  • 把求解的问题分成若干个子问题。
  • 对每一子问题求解,得到子问题的局部最优解。
  • 把子问题的解局部最优解合成原来解问题的一个解。

活动安排问题定义

定义(活动)

设有n个活动的集合 E={1,2,…,n},其中每个活动都要求使用同一资源,而在同一时间内只有一个活动能使用这个资源。

定义(相容活动)

  • 若区间 [start_i, finish_i) 与区间 [start_j, finish_j) 不相交,则称活动i与活动j是相容的。也就是说,当 start_i >= finish_jstart_j >= finish_i 时,活动i与活动j相容。
就像你在学校的课程表上安排不同的课程,
-如果两节课的时间没有重叠,
你就可以同时选这两节课,它们就是相容的。
-如果两节课有重叠,比如一节课是8点到10点,另一节课是9点到11点,
那么这两节课就不能同时选,因为它们不兼容。

在这里插入图片描述
在这里插入图片描述

活动安排问题的贪心算法思路

  1. 将各个活动按照活动结束时间 finishi 从小到大排序,且 finish1 <= finish2 <= finish3 …
  2. 选择活动1,要求该活动的结束时间最早。
  3. 从2开始按顺序比较各个活动,选择第一个与活动1相容的活动i,下一个活动的开始时间大于等于上一个活动的结束时间。
  4. 从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;
            }
        }
    }
}


在这里插入图片描述

复杂度分析

()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值