经典贪心法:时间序列问题及其全局最优性证明

贪心算法是指在对问题求解时,总做出在当前看来是最好的选择。也就是说,不从整体上加以考虑,它所作出的仅仅是在某种意义上的局部最优解。一旦贪心算法求出了一个可行解,就要确定这个算法是否找到了最优解。为此,要么证明这个解是最优的,要么说明一个该算法产生非最优解的反例。

为了更方便的说明问题,我们来拿一个实例做分析,下面的题目取自HDU_2037:今年暑假不AC

Problem Description
“今年暑假不AC?” “是的。” “那你干什么呢?” “看世界杯呀,笨蛋!” “@#$%^&*%...”
确实如此,世界杯来了,球迷的节日也来了,估计很多ACMer也会抛开电脑,奔向电视了。 作为球迷,一定想看尽量多的完整的比赛,当然,作为新时代的好青年,你一定还会看一些其它的节目,比如新闻联播(永远不要忘记关心国家大事)、非常6+7、超级女生,以及王小丫的《开心辞典》等等,假设你已经知道了所有你喜欢看的电视节目的转播时间表,你会合理安排吗?(目标是能看尽量多的完整节目)

Input
输入数据包含多个测试实例,每个测试实例的第一行只有一个整数n(n<=100),表示你喜欢看的节目的总数,然后是n行数据,每行包括两个数据Ti_s,Ti_e (1<=i<=n),分别表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。n=0表示输入结束,不做处理。

Output
对于每个测试实例,输出能完整看到的电视节目的个数,每个测试实例的输出占一行。
Sample Input
12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
0
Sample Output
5

仔细考察问题(自己举一些简单的例子来找规律)后我们发现:在算法的每个阶段,都从可以完整收看的节目中选择一个最早结束的节目观看。我们将证明在从能看尽量多的完整节目的意义上而言,这种贪心算法是一种最优算法。为了证明该算法的最优性,对变量n应用数学归纳法,其中n是算法中的节目数。设P(n)是命题:如果贪心算法安排了n个节目,那么不可能安排更多的节目。

  基础步骤:设应用上述贪婪算法只安排了1个节目(Ti_s,Ti_e (1<=i<=n),分别表示第i个节目的开始和结束时间),这说明所有其他的节目的开始时间都在安排的那1个节目的结束时间T1_e之前,并且结束时间Ti_e <= T1_e。这又说明了什么?说明任意两两节目的播出时间都有重叠,因此如果贪心算法安排了1个节目,那么不可能安排更多的节目。

  归纳步骤:归纳步骤是P(k)为真,其中k是一个正整数。也就是说,如果贪心算法安排了k个节目,那么不可能安排更多的节目。下面我们需要证明在k成立的条件下,命题对k+1仍然成立。换句话说,假设贪心算法安排了k+1个节目,那么我们需要证明不能安排更多的节目了。我们以安排尽量多的节目为出发点,那么第一个节目应该是最早结束的那个,因为所有节目的开始和结束时间都已经给定了,因此要想尽可能多的安排节目我们需要在安排了第一个节目后使剩下的时间最大化,即第一个节目是众多节目中最早结束的那个能保证安排节目的数量达到最大化。剩下的问题就好办了,剩下的时间安排k个节目已经由我们的归纳假设P(k)保证了。

OK,我们已经完成了基础步骤和归纳步骤,根据数学归纳法的原理可知:对所有正整数n,P(n)为真。这就完成了最优性的证明。

确定了对于该问题使用贪心法能保证找到一个最优解后,我们可以踏踏实实地编写程序来实现了:

#include<stdio.h>
#include<string.h>
void bubble_sort(int a[][2], int); // 注意二维数组做形参的写法
int main(void)
{
    int n, i, count, first, second;
    int a[110][2];
    while(scanf("%d", &n) && n) // n==0,输入结束
    {
        i = 0;
        memset(a, 0, sizeof(a));
        while(n--)
        {
            scanf("%d%d", &a[i][0], &a[i][1]);
            i++;
        }

        bubble_sort(a, i); // i为数据的个数

        // 排好序后,从最早结束的开始,找第二早结束的,如果满足前者结束时间<=后者开始时间,观看节目数加一,重复上述过程,直至遍历完所有给出的节目数据。

        count = 1;
        first = 0;
        second = 1;
        while(1)
        {
            if(a[first][1] <= a[second][0])
            {
                count++;
                first = second;
            }
            second++;
            if(second >= i)
                break;
        }
        printf("%d\n", count);
    }
    return 0;
}

void bubble_sort(int a[][2], int n)
{
    int temp;
    for(int outer = n-1; outer > 0; outer--)
        for(int inter = 0; inter < outer; inter++)
        {
            if(a[inter][1] > a[inter+1][1])
            {
                temp = a[inter][1];
                a[inter][1] = a[inter+1][1];
                a[inter+1][1] = temp;
                temp = a[inter][0];
                a[inter][0] = a[inter+1][0];
                a[inter+1][0] = temp;
            }
        }
}
All Rights Reserved.
Author:海峰:)
Copyright © xp_jiang.
转载请标明出处:http://www.cnblogs.com/xpjiang/p/4413903.html以上.
时间: 2024-09-21 02:33:33

经典贪心法:时间序列问题及其全局最优性证明的相关文章

8.4 贪心法

1.背包问题: ①最优装载问题:把物体重量从小到大排列,依次选择每个物体,只顾眼前,却能得到最优解. ②部分背包问题:把物体的"价值除以重量的值"从小到大排序,一次选择每个物体(贪心只能对一个变量贪心,这是一种巧妙的转换). ③乘船问题:只让眼前的浪费最少.(注意是让什么最少,是让浪费最少!) 2.区间相关问题(排序:排左边还是右边?): ①选择不相交区间: ②区间选点问题: ③区间覆盖问题: 3.定义: 在对问题求解时,总是做出在当前看来是最好的选择.也就是说,不从整体最优上加以考虑

贪心法

贪心法(Greedy Approach)又称贪婪法, 在对问题求解时,总是做出在当前看来是最好的选择,或者说是:总是作出在当前看来最好的选择.也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择.当然,希望贪心算法得到的最终结果也是整体最优的.虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解.如单源最短路经问题,最小生成树问题等.在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似. 贪心法的设计思想 当一个问题具有以

贪心法 codevs 1052 地鼠游戏

1052 地鼠游戏 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 王钢是一名学习成绩优异的学生,在平时的学习中,他总能利用一切时间认真高效地学习,他不但学习刻苦,而且善于经常总结.完善自己的学习方法,所以他总能在每次考试中得到优异的分数,这一切很大程度上是由于他是一个追求效率的人. 但王钢也是一个喜欢玩的人,平时在学校学习他努力克制自己玩,可在星期天他却会抽一定的时间让自己玩一下,他的爸爸妈妈也比较信任他的学习能力

[LeetCode]wildcard matching通配符实现之贪心法

前天用递归LTE,昨天用动态规划LTE,今天接着搞,改用贪心法.题目再放一次: '?'匹配任意字符,'*'匹配任意长度字符串 Some examples: isMatch("aa","a") → false isMatch("aa","aa") → true isMatch("aaa","aa") → false isMatch("aa", "*"

C语言(贪心法)

C语言有这样一个规则,每一个符号应该包含尽可能多的字符.也就是说,编译器将程序分解成符号的方法是,从左到右一个一个字符地读入,如果字条可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分,如果可能,继续读入下一个字条,重复上述判断,直到读入的字符组成的字符串已经不再可能组成一个有意义的符号.这个处理的策略被称为"贪心法".需要注意的是,除了字符串与字符常量,符号的中间不能嵌有空白(空格.制表符.换行符等). 看一下下面的代码:想一下输出

算法题解之贪心法

Wiggle Subsequence 最长扭动子序列 思路1:动态规划.状态dp[i]表示以nums[i]为末尾的最长wiggle子序列的长度.时间是O(n^2). 1 public class Solution { 2 public int wiggleMaxLength(int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return 0; 5 } 6 int[] pos_dp = new int[nums.length]; 7

贪心法-c语言的规则

在面试的过程中,有很多的考验对c的认识的情况,有时会被问到有关字符搭配以及运算先后顺序的问题,比如a+++++b的值,++i+++i+++i+i的值等类似的,这都属于c的符号方面的问题,那么怎样才能轻而易举的去认识它呢? c语言有这样的一个规则:那就是传说中的贪心法,规则是这样定的:每个符号应该包含尽可能多的字符,也就是说,我们的编译器将程序分解成符号的方法是,从左到右一个一个字符的读入,如果该字符可能组成一个符号,那么再读入下一个字符,然后在判断已经读入的两个字符是否有可能是一个符号或者一个符

ACM:贪心法:乘船问题。

题目:有n个人,第i个人的重量为wi,每艘船的最大载重量均为C,且最多只能乘两个人.用最少的船装载所有人. 分析:贪心法! 考虑最轻的人i,他应该和谁一起坐呢?如果每个人都无法和他一起坐船,那么唯一的方案就是每个人坐一艘船! 否则,他应该选择能和他一起坐船的人中最重的一个j. 这样的方法是贪心的!因为:它只是让"眼前"的浪费最少. 程序实现:我们只需用两个下标i和j分别表示当前考虑的最轻的人和最重的人,每次先将j往左移动,直到i和j可以共坐一艘船,然后i加1,j减1.并且重复上述操作!

理解动态规划、分治法和贪心法

本文转自:http://www.cnblogs.com/airwindow/p/4067902.html http://hi.baidu.com/35661327/blog/item/d5463e17f1e8d011972b439c.html 动态规划.分治法和贪心法都是利用求解子问题,而后利用子问题求解更上层问题,最终获得全局解决方案的方法. 但是三者的应用场景和性质却存在着极大的不同: 1.分治法 很容易与动态规划问题混淆,但两者却有着本质上的差异. 分治法采用的是递归的思想来求解问题,两个