步步为营(三)贪心(2)选择不相交区间

来源:互联网 发布:java 捕获异常 编辑:程序博客网 时间:2024/05/14 16:25

之前基本了解了贪心的基本思想,现在我们来看一下比较经典的几个贪心问题。
这篇文章讨论的是”选择不相交区间“,具体什么意思,我们同样先看一道题。


题目来源:NYOJ 14

                            会场安排问题                时间限制:3000 ms  |  内存限制:65535 KB                            难度:4(这个难度真的很唬人~~)

描述
学校的小礼堂每天都会有许多活动,有时间这些活动的计划时间会发生冲突,需要选择出一些活动进行举办。小刘的工作就是安排学校小礼堂的活动,每个时间最多安排一个活动。现在小刘有一些活动计划的时间表,他想尽可能的安排更多的活动,请问他该如何安排。

输入
第一行是一个整型数m(m<100)表示共有m组测试数据。
每组测试数据的第一行是一个整数n(n<1000)表示该测试数据共有n个活动。
随后的n行,每行有两个正整数Bi,Ei(0<=Bi,Ei<10000),分别表示第i个活动的起始与结束时间(Bi<=Ei)

输出
对于每一组输入,输出最多能够安排的活动数量。
每组的输出占一行
样例输入
2
2
1 10
10 11
3
1 10
10 11
11 20
样例输出
1
2

提示
注意:如果上一个活动在t时间结束,下一个活动最早应该在t+1时间开始


首先,把题目中的信息转换为模型。
给你n个区间,求得不相交区间的数目。(P.S.也就是一句话的事~~题目太经典,无需多言)

下面我们来分析这道题是否能用贪心的方法来做,让我们回顾贪心的三个关键点

1.问题可以分解为多个子问题。

那么当前问题可不可以? 当然可以,选择不相交区间的过程,完全可以分解为:给定一个区间,按照某种规则对接下来的多个区间进行比较,然后得出最优结果的过程。
这里的规则指的是:对于当前区间来说,优先选择离当前区间距离最近而且区间最短的下一个区间。

2.总问题的最优解可由各个子问题的最优解得到。

这个也很好理解,由于每个子问题求得的永远是离当前区间距离最近而且区间最短的下一个区间,而下一个区间也是按照这个规则来寻找另外一个区间的。所以对于最优解集合的解释就是“对于每一个区间来说,后面的那个区间就是离它最近(空白少)而且区间最短(占用少)的”,这也就是说,满足总体问题最优解的“尽可能多的选择区间

3.子问题具有”无后效性“,当前子问题的求解不会影响到之前子问题的解。

这个题目乍一看貌似是不满足无后效性的,因为给定的数据顺序混乱,似乎每一次寻找都要进行一次遍历,但是这个过程可以通过排序的方式解决。这种处理原始数据,使得处理过程更加简单快捷的方法,叫做预处理

综上,这道题(或者说这类问题)确实是可以通过贪心策略来取得最终的解得。
那么,预处理过程应该怎么排序呢?
我们再来聊一聊最优解的判定规则,也就是两点:
1.区间最短(长度)
2.间隔最小(两端位置)

那么我们是按照哪一种指标来进行排序呢?答案很明显,长度肯定是不靠谱的。因为单单通过长度进行排序,我们并不能对两端位置进行控制,并没有减少我们对于数据的判定过程,反而可能会加大难度。

那么我们按照两端位置进行排序,排序的规则很明显,按照从小到大的顺序进行排序。
网络上很多人采用以后端点排序的方式,可能会让刚接触的人误以为只能通过后断电排序,其实不然,采用哪个端点排序都是可以的。而不管选择哪一个来排序,其原理和本质都一样,都是为了方便操作,将其有序化。

那么我们的贪心策略可以用伪代码描述为:

while(剩余区间的数目不为0){    if(找到符合条件的下一个区间)    {        当前区间 = 下一个区间;        答案数+1;    }    区间数--;}

话说至此,这道题目我们就可以开始做了。

下面附上AC代码:

/*************************************    Title: NYOJ41--会场安排问题************************************    Date:2015/07/120************************************    author:刘旭************************************Memory:308KBTime:80ms*************************************/#include<stdio.h>#include<algorithm>using namespace std;#define MAX 10050struct Node{    int x,y;} map[10005];  /*定义结构体数组*/int cmp(Node a,Node b){    if(a.y!=b.y)    {        return a.y<b.y;    }    return a.x<b.x;}int main(){    int m = 0;    scanf("%d",&m);    while(m--)    {        int  num = 0;        scanf("%d",&num);        for(int i = 0; i < num; i++)        {            scanf("%d%d",&map[i].x,&map[i].y);        }        sort(map,map+num,cmp);        int start = map[0].y;   ///起始位置        int count=1;    ///答案数        int num_last = num;///剩余区间数目        while(num_last > 0)        {            if(map[num - num_last].x > start)            {                count++;                start = map[num - num_last].y;            }            num_last--;        }        printf("%d\n",count);    }    return 0;}

行文至此,整个问题便已解决。说句实话,多写写分析什么的,确实挺爽的~

0 0