当前位置:首页 > 算法 > 正文内容

【贪心】区间调度

亿万年的星光5年前 (2021-01-30)算法4561

【题目描述】

有n项工作,每项工作分别在si时间开始,在ti时间结束。对于每项工作,你都可以选择参与与否。如果选择了参与,那么自始至终都必须全程参与。此外,参与工作的时间段不能重叠(即使是开始与结束的瞬间重叠也是不允许的)。

你的目标是参与尽可能多的工作,那么最多能参与多少项工作呢?1<=n<=100000,1<=si<=ti<=10^9。

【输入】

n

n项工作的开始与结束时间

【输出】

最多参与的工作项数

【输入样例】

5
1 3
2 5
4 7
6 9
8 10

【输出样例】

3



【分析】

这个问题等价在问,数轴上有n个区间,选出最多的区间,使得这些区间不互相重叠,

我们可以用下面这个图示过程来表示:


此问题可通过贪心算法求解,我们容易想到以下4种算法

(1)在可选的工作中,每次都选取开始时间最早的工作

(2)在可选的工作中,每次都选取结束时间最早的工作

(3)在可选的工作中,每次都选取用时最短的工作

(4)在可选的工作中,每次都选取与最少可选工作有重叠的工作

显然(2)是正确的,其它3种算法都易举出反例;或者说在有些情况下,它们并不一定能得到最优解。

另一种解释:

1)与其它选择方案相比,该算法的选择方案在选取了相同数量的更早时间的工作时,其最终结束时间不会比其它方案更晚。

2)所以,不存在选取更多工作的选择方案。

证明:

显然,该算法最后选出的区间不互相重叠,下面证明所选出区间的数量是最多的。设fi为该算法所接受的第i个区间的右端点坐标,gi为某最优解中的第i个区间的右端点坐标。

命题1.1 

 当i>=1时,该算法所接受的第i个区间的右端点坐标fi<=某最优解中的第i个区间的右端点坐标gi。该命题可以运用数学归纳法来证明。对于i=1,命题显然为真,因为算法第一个选择的区间拥有最小右端点坐标。令i>1,假定论断对i-1为真,即fi-1<=gi-1。则最优解的第i个可选区间所组成的集合包含于执行该算法时第i个可选区间所组成的集合;而当算法选择第i个区间时,选的是在可选区间中右端点坐标最小的一个,所以有fi<=gi。证毕。设该算法选出了k个区间,而最优解选出了m个区间。

命题1.2  

最优解选出的区间数量m=该算法选出的区间数量k。假设m>k,根据命题1.1,有fk<=gk。由于m>k,必然存在某区间,在gk之后开始,故也在fk之后开始。而该算法一定不会在选了第k个区间后停止,还会选择更多的区间,产生矛盾。所以m<=k,又因为m是最优解选出区间个数,所以m=k。综上所述,算法选出的区间是最优解。

【算法与结论】:

将所有区间按右端点坐标从小到大排序,顺序处理每个区间。如果它与当前已选的所有区间都没有重叠,则选择该区间,否则不选。



【参考答案1】

#include <iostream>  
#include <cstdio>  
#include <algorithm>  
using namespace std;  
const int maxn=100005;  
int N;  
int ans=0;  
struct Work   //定义工作   
{  
    int s;    //开始时间   
    int t;    //结束时间   
}w[maxn];  

bool cmp( Work w1,Work w2)  
//按工作结束时间递增排序   
{  
   return (w1.t<w2.t);  
}  
int main()  
{  
    int i,j;  
    int end;  //end记录最后所选活动的结束时间  
    scanf("%d",&N);  
    for(i=0;i<N;i++)  
        scanf("%d %d",&w[i].s,&w[i].t);  

    sort(w,w+N,cmp);  
    j=ans=0;  
    end=0;  
    for(j=0;j<N;j++)  
    {  
        if(w[j].s>end)  //若发现某项工作的开始时间比end晚   
        {  
            ans++;      //则选择当前工作   
            end=w[j].t; //将这项工作的结束时间作为end?  
        }  
    }  
    printf("%d\n",ans);  
    return 0;  
}


扫描二维码推送至手机访问。

版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。

分享给朋友:

相关文章

【算法】动态规划(一)

1.基本概念在现实生活中,有一类活动的过程,由于它的特殊性,可将过程分成若干个互相联系的阶段,在它的每一阶段都需要作出决策,从而使整个过程达到最好的活动效果。因此各个阶段决策的选取不能任意确定,它依赖...

【算法】最短路径算法——Floyed-Warshell算法

【算法】最短路径算法——Floyed-Warshell算法

如下图所示,我们把边带有权值的图称为带权图。边的权值可以理解为两点之间的距离。一张图中任意两点间会有不同的路径相怜。最短路径就是指连接两点的这些路径中最短的一条。【注意】边的权值可以为负。当出现负边权...

图论—拓扑排序

前序文章:拓扑排序 - C++目录 - 青少年编程知识记录一、简述拓扑排序是针对 有向无环图(DAG, Directed Acyclic Graph) 的一种排序算法,其核心目标是...

【算法】高精度(1)

一、  什么是高精度高精度算法,属于处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算...

【贪心】----排队打水

【贪心】----排队打水

一、基础版排队打水【题目描述】学校里有一个水房,水房里一共装有m 个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为1。现在有n 名同学准备接水,他们的初始接水顺序已经确定。...

【图论】迪杰斯特拉算法

【图论】迪杰斯特拉算法

迪杰斯特拉算法是由荷兰计算机科学家艾兹赫尔・迪杰斯特拉于 1956 年提出的单源最短路径算法,用于求解带权有向、 无向图中,从一个源节点到其余所有节点的最短路径问题(要求图中所有边的权值非负)。一、核...