Expm 7_2区间调度问题
【问题描述】
给定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区间调度问题的更多相关文章
- hdu 2037 - 今年暑假不AC(区间调度问题)
题意:区间调度问题 解法:应用贪心算法,贪心的规则: 在可选的节目中,选取结束时间早的节目. 1: #include<stdlib.h> 2: #include<string.h&g ...
- 编程算法 - 区间调度问题 代码(C)
区间调度问题 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 有n项工作, 每项工作分别在s时间開始, 在t时间结束. 对于每项工作能够选择參与 ...
- ACM_区间调度问题(贪心)
Meetings 系列一 Time Limit: 2000/1000ms (Java/Others) Problem Description: 多年之后的广财ACM编协如日中天,下系多个部门,且编协成 ...
- Expm 8_1 区间划分问题
[问题描述] 给定一组报告,其中的每个报告设置了一个开始时间si和结束时间fi.设计与实现一个算法,对这组报告分配最少数量的教室,使得这些报告能无冲突的举行. package org.xiu68. ...
- hdu2037 今年暑假不AC[贪心][区间调度问题]
目录 题目地址 题干 代码和解释 参考 题目地址 hdu2037 题干 代码和解释 本题使用贪心.有三种贪心策略:开始时间最早,结束时间最早,用时最短.第二种是正确的策略,因为结束得越早,后面就可以有 ...
- 贪心算法----区间选点问题(POJ1201)
题目: 题目的大致意思是,给定n个闭区间,并且这个闭区间上的点都是整数,现在要求你使用最少的点来覆盖这些区间并且每个区间的覆盖的点的数量满足输入的要求点覆盖区间的数量. 输入: 第一行输入n,代表n个 ...
- pair<>结构体模版的用法
1.pair算是一个结构体模版,定义的时候是这样的: pair<T1,T2> P; 其中T1,T2可以是int,string,double,甚至是vector<>. 2.进行初 ...
- nomasp 博客导读:Android、UWP、Algorithm、Lisp(找工作中……
Profile Introduction to Blog 您能看到这篇博客导读是我的荣幸.本博客会持续更新.感谢您的支持.欢迎您的关注与留言.博客有多个专栏,各自是关于 Android应用开发 .Wi ...
- toj2867 Picking Problem
题目链接:http://acm.tju.edu.cn/toj/showp.php?pid=2867 题目大意:给定一系列活动的开始时间和结束时间,问最多能参加的活动数目 思路:// 本题属于最大区间调 ...
随机推荐
- A1069. The Black Hole of Numbers
For any 4-digit integer except the ones with all the digits being the same, if we sort the digits in ...
- dijkstra(最短路)和Prim(最小生成树)下的堆优化
dijkstra(最短路)和Prim(最小生成树)下的堆优化 最小堆: down(i)[向下调整]:从第k层的点i开始向下操作,第k层的点与第k+1层的点(如果有)进行值大小的判断,如果父节点的值大于 ...
- instance of
instanceof是Java的一个二元操作符(运算符),也是Java的保留关键字.它的作用是判断其左边对象是否为其右边类的实例,返回的是boolean类型的数据.用它来判断某个对象是否是某个Clas ...
- 线程的状态有哪些,线程中的start与run方法的区别
线程在一定条件下,状态会发生变化.线程一共有以下几种状态: 1.新建状态(New):新创建了一个线程对象. 2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法. ...
- shell中的循环语法
shell中的循环语法 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.for循环 1.语法格式1 for 变量 in 值1 值2 值3 ... do ...
- mysql -- 动态获取结果集(重点)
注意:语句传值的时候必须是带有@符号的参数,不能是自己的局部变量,一个@叫用户变量,两个@叫做全局变量.用户变量:当前用户的‘’全局变量‘’,用户状态存在时就存在,用户退出时消失. 初始版 delim ...
- Linux 命令详解(六)Linux 守护进程的启动方法
Linux 守护进程的启动方法 http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html
- html接收参数
代码 <!DOCTYPE html> <html> <head> <title>html接收参数</title> </head> ...
- ping不同网站总结
用高级设置法预防Ping 用网络防火墙阻隔Ping 启用IP安全策略防Ping IP安全机制(IP Security)即IPSec策略,用来配置IPSec安全服务.这些策略可为多数现有网络中地多数通信 ...
- 【洛谷P1052【NOIP2005提高T2】】过河
题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是正整数,我们可以把独木桥上青蛙可能到达的点看成数 ...