【问题描述】

给定n个活动,其中的每个活动ai包含一个起始时间si与结束时间fi。设计与实现算法从n个活动中找出一个最大的相互兼容的活动子集S。

要求:分别设计动态规划与贪心算法求解该问题。其中,对贪心算法分别给出递归与迭代两个版本的实现。

动态规划版本描述:

下面我们再换个角度考虑上面的问题。很多最优化深搜问题都可以巧妙地转化成动态规划问题,可以转化的根本原因在于存在重复子问题,我们看图四就会发现最多区间调度问题也存在重复子问题,所以可以利用动态规划来解决。假设区间已经排序,可以尝试这样设计递归式:前i个区间的最多不重叠区间个数为dp[i]。dp[i]等于啥呢?我们需要根据第i个区间是否选择这两种情况来考虑。如果我们选择第i个区间,它可能和前面的区间重叠,我们需要找到不重叠的位置k,然后计算最多不重叠区间个数dp[k]+1(如果区间按照开始时间排序,则前i+1个区间没有明确的分界线,我们必须按照结束时间排序);如果我们不选择第i个区间,我们需要从前i-1个结果中选择一个最大的dp[j];最后选择dp[k]+1和dp[j]中较大的。

选择或者不选择第i个区间都需要去查找其他的区间,顺序查找的复杂度为O(n),总共有n个区间,每个区间都需要查找,所以动态规划部分最初的算法复杂度为O(n2),已经从指数级降到多项式级,但是经过后面的优化还可以降到O(n),我们一步步来优化。

可以看出dp[i]是非递减的,这可以通过数学归纳法证明。也即当我们已经求得前i个区间的最多不重叠区间个数之后,再求第i+1个区间时,我们完全可以不选择第i+1个区间,从而使得前i+1个区间的结果和前i个区间的结果相同;或者我们选择第i+1个区间,在不重叠的情况下有可能获得更优的结果。dp[i]是非递减的对我们有什么意义呢?首先,如果我们在计算dp[i]时不选择第i个区间,则我们就无需遍历前i-1个区间,直接选择dp[i-1]即可,因为它是前i-1个结果中最大的(虽然不一定是唯一的),此时伪代码中的dp[j]就变成了dp[i-1]。其次,在寻找和第i个区间不重叠的区间时,我们可以避免顺序遍历。如果我们将dp[i]的值列出来,肯定是这样的:

1,1,…,1,2,2,…,2,3,3,…,3,4……

即dp[i]的值从1开始,顺次递增,每一个值的个数不固定。dp[0]肯定等于1,后面几个区间如果和第0个区间重叠,则的dp值也为1;当出现一个区间不和第0个区间重叠时,其dp值变为2,依次类推。由此我们可以得到一个快速获得不重叠位置的方法:重新开辟一个新的数组,用来保存每一个不同dp值的最开始位置,例如pos[1]=0,pos[2]=3,…。这样我们就可以利用O(1)的时间实现find_nonoverlap_pos函数了,然后整个动态规划算法的复杂度就降为O(n)了。

其实从dp的值我们已经就可以发现一些端倪了:dp值发生变化的位置恰是出现不重叠的位置!再仔细思考一下就会出现一开始提到的贪心算法了。所以可以说,贪心算法是动态规划算法在某些问题中的一个特例。该问题的特殊性在于只考虑区间的个数,也即每次都是加1的操作,后面会看到,如果变成考虑区间的长度,则贪心算法不再适用。

 package org.xiu68.exp.exp7;

 public class Task{
public int startTime; //开始时间
public int endTime; //结束时间 public Task(int startTime,int endTime){
this.startTime=startTime;
this.endTime=endTime;
}
}
 package org.xiu68.exp.exp7;

 import java.util.ArrayList;

 public class Exp7_2_1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Task> tasks=new ArrayList<>();
tasks.add(new Task(1,5));
tasks.add(new Task(2,4));
tasks.add(new Task(3,6));
tasks.add(new Task(5,8));
intervalSchedule(tasks);
} public static void intervalSchedule(ArrayList<Task> tasks){
//按结束时间从小到大进行排序
tasks.sort((t1,t2)->{
if(t1.endTime<=t2.endTime)
return -1;
else
return 1;
}); Task[] t = new Task[tasks.size()];
tasks.toArray(t);
int result = 0;
if(t.length>=1){
result=1+intervalRecuisive(t, 0, t.length-1);
}
System.out.println("最大区间个数为: "+result);
}
public static int intervalRecuisive(Task[] task,int i,int j){
int m=i+1;
//下一个任务与前面的任务不兼容
while(m<=j && task[m].startTime<task[i].endTime)
m++;
//已经没有任务与前面任务兼容
if(m<=j)
return 1+intervalRecuisive(task, m, j);
else
return 0;
}
}
 package org.xiu68.exp.exp7;

 import java.util.ArrayList;

 public class Exp7_2_2 {

     //区间调度的迭代版本(最大区间个数)
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Task> tasks=new ArrayList<>();
tasks.add(new Task(6,9));
tasks.add(new Task(6,7));
tasks.add(new Task(5,6));
tasks.add(new Task(4,5));
intervalSchedule(tasks);
} public static void intervalSchedule(ArrayList<Task> tasks){
//按结束时间从小到大进行排序
tasks.sort((t1,t2)->{
if(t1.endTime<=t2.endTime)
return -1;
else
return 1;
}); int result=0,end=0;
for(int i=0;i<tasks.size();i++){ //每次选择结束时间最早的任务
if(tasks.get(i).startTime>=end){ //如果任务之间相互兼容
result+=1;
end=tasks.get(i).endTime;
}
}
System.out.println("最大区间个数为: "+result);
}
}

Expm 7_2区间调度问题的更多相关文章

  1. hdu 2037 - 今年暑假不AC(区间调度问题)

    题意:区间调度问题 解法:应用贪心算法,贪心的规则: 在可选的节目中,选取结束时间早的节目. 1: #include<stdlib.h> 2: #include<string.h&g ...

  2. 编程算法 - 区间调度问题 代码(C)

    区间调度问题 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 有n项工作, 每项工作分别在s时间開始, 在t时间结束. 对于每项工作能够选择參与 ...

  3. ACM_区间调度问题(贪心)

    Meetings 系列一 Time Limit: 2000/1000ms (Java/Others) Problem Description: 多年之后的广财ACM编协如日中天,下系多个部门,且编协成 ...

  4. Expm 8_1 区间划分问题

      [问题描述] 给定一组报告,其中的每个报告设置了一个开始时间si和结束时间fi.设计与实现一个算法,对这组报告分配最少数量的教室,使得这些报告能无冲突的举行. package org.xiu68. ...

  5. hdu2037 今年暑假不AC[贪心][区间调度问题]

    目录 题目地址 题干 代码和解释 参考 题目地址 hdu2037 题干 代码和解释 本题使用贪心.有三种贪心策略:开始时间最早,结束时间最早,用时最短.第二种是正确的策略,因为结束得越早,后面就可以有 ...

  6. 贪心算法----区间选点问题(POJ1201)

    题目: 题目的大致意思是,给定n个闭区间,并且这个闭区间上的点都是整数,现在要求你使用最少的点来覆盖这些区间并且每个区间的覆盖的点的数量满足输入的要求点覆盖区间的数量. 输入: 第一行输入n,代表n个 ...

  7. pair<>结构体模版的用法

    1.pair算是一个结构体模版,定义的时候是这样的: pair<T1,T2> P; 其中T1,T2可以是int,string,double,甚至是vector<>. 2.进行初 ...

  8. nomasp 博客导读:Android、UWP、Algorithm、Lisp(找工作中……

    Profile Introduction to Blog 您能看到这篇博客导读是我的荣幸.本博客会持续更新.感谢您的支持.欢迎您的关注与留言.博客有多个专栏,各自是关于 Android应用开发 .Wi ...

  9. toj2867 Picking Problem

    题目链接:http://acm.tju.edu.cn/toj/showp.php?pid=2867 题目大意:给定一系列活动的开始时间和结束时间,问最多能参加的活动数目 思路:// 本题属于最大区间调 ...

随机推荐

  1. Flash10 使用剪贴板得改变程序的写法了

    昨天一个客户告诉我,在她的电脑上无法复制图片的链接地址. 一开始,我以为是她操作有误,因为在我们的系统里使用的是一种“双保险”的复制方法. javascript + flash 两种方法来进行复制. ...

  2. 蛋白质结构模型和功能预测:I-TASSER工具的使用

    I-TASSER是一款用于预测蛋白质结构和功能的工具,网站链接:https://zhanglab.ccmb.med.umich.edu/I-TASSER/ 具体描述如下: I-TASSER (Iter ...

  3. 对信号量Semaphore的理解与运用

    转: java笔记--对信号量Semaphore的理解与运用 java Semaphore 信号量的使用: 在java中,提供了信号量Semaphore的支持. Semaphore类是一个计数信号量, ...

  4. Qt shortcuts

    ESC 切换到代码编辑模式 F1   查看帮助 F4    .h 与 .c or .cpp之间切换 shift + F2 声明与定义间切换 Ctrl + tab 切换已打开的文件 Ctrl + B 编 ...

  5. 1021. Deepest Root (25)

    A graph which is connected and acyclic can be considered a tree. The height of the tree depends on t ...

  6. 解决编译安装php时报错:Please reinstall the iconv library

    编译安装php7时报错“Please reinstall the iconv library”,也就是让重新安装iconv库.但yum安装又提示“No package libiconv availab ...

  7. 关于js事件执行顺序小技巧

    js事件执行顺序是js中一个老生常谈的一个话题, 聊这个话题之前我们先谈谈怎么给页面元素绑定我们需要的事件 1.给页面元素绑定事件 a)直接在元素上面加上需要绑定的事件,如 <button ty ...

  8. Java编程思想 学习笔记10

    十.内部类  可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性.然而必须要了解,内部类和组合是 ...

  9. WebMvcConfigurerAdapter已过时,替换接口或类

    WebMvcConfigurerAdapter已经过时,在新版本2.x中被废弃,原因是springboot2.0以后,引用的是spring5.0,而spring5.0取消了WebMvcConfigur ...

  10. Element link doesn't have required attribute property

    前端标准http://validator.w3.org/ 拒绝你的代码时报 Element link doesn't have required attribute property 把样式链接 &l ...