• 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的操作,后面会看到,如果变成考虑区间的长度,则贪心算法不再适用。

     1 package org.xiu68.exp.exp7;
     2 
     3 public class Task{
     4     public int startTime;    //开始时间
     5     public int endTime;        //结束时间
     6     
     7     public Task(int startTime,int endTime){
     8         this.startTime=startTime;
     9         this.endTime=endTime;
    10     }
    11 }
    View Code
     1 package org.xiu68.exp.exp7;
     2 
     3 import java.util.ArrayList;
     4 
     5 public class Exp7_2_1 {
     6     public static void main(String[] args) {
     7         // TODO Auto-generated method stub
     8         ArrayList<Task> tasks=new ArrayList<>();
     9         tasks.add(new Task(1,5));
    10         tasks.add(new Task(2,4));
    11         tasks.add(new Task(3,6));
    12         tasks.add(new Task(5,8));
    13         intervalSchedule(tasks);
    14     }
    15 
    16     public static void intervalSchedule(ArrayList<Task> tasks){
    17         //按结束时间从小到大进行排序
    18         tasks.sort((t1,t2)->{
    19             if(t1.endTime<=t2.endTime)
    20                 return -1;
    21             else
    22                 return 1;
    23         });
    24         
    25         Task[] t = new Task[tasks.size()];
    26         tasks.toArray(t);
    27         int result = 0;
    28         if(t.length>=1){
    29             result=1+intervalRecuisive(t, 0, t.length-1);
    30         }
    31         System.out.println("最大区间个数为: "+result);
    32     }
    33     public static int intervalRecuisive(Task[] task,int i,int j){
    34         int m=i+1;
    35         //下一个任务与前面的任务不兼容
    36         while(m<=j && task[m].startTime<task[i].endTime)
    37             m++;
    38         //已经没有任务与前面任务兼容
    39         if(m<=j)
    40             return 1+intervalRecuisive(task, m, j);
    41         else
    42             return 0;
    43     }
    44 }
    View Code
     1 package org.xiu68.exp.exp7;
     2 
     3 import java.util.ArrayList;
     4 
     5 public class Exp7_2_2 {
     6 
     7     //区间调度的迭代版本(最大区间个数)
     8     public static void main(String[] args) {
     9         // TODO Auto-generated method stub
    10         ArrayList<Task> tasks=new ArrayList<>();
    11         tasks.add(new Task(6,9));
    12         tasks.add(new Task(6,7));
    13         tasks.add(new Task(5,6));
    14         tasks.add(new Task(4,5));
    15         intervalSchedule(tasks);
    16     }
    17     
    18     public static void intervalSchedule(ArrayList<Task> tasks){
    19         //按结束时间从小到大进行排序
    20         tasks.sort((t1,t2)->{
    21             if(t1.endTime<=t2.endTime)
    22                 return -1;
    23             else
    24                 return 1;
    25         });
    26         
    27         int result=0,end=0;
    28         for(int i=0;i<tasks.size();i++){        //每次选择结束时间最早的任务
    29             if(tasks.get(i).startTime>=end){        //如果任务之间相互兼容
    30                 result+=1;
    31                 end=tasks.get(i).endTime;
    32             }
    33         }
    34         System.out.println("最大区间个数为: "+result);
    35     }
    36 }
    View Code
  • 相关阅读:
    Spring Boot (五): Redis缓存使用姿势盘点
    跟我学SpringCloud | 第二十章:Spring Cloud 之 okhttp
    Spring Boot (四): Druid 连接池密码加密与监控
    跟我学SpringCloud | 第十九章:Spring Cloud 组件 Docker 化
    跟我学SpringCloud | 第十八篇:微服务 Docker 化之基础环境
    Spring Boot (三): ORM 框架 JPA 与连接池 Hikari
    Spring Cloud Alibaba | Dubbo Spring Cloud 之 HTTP 实战
    Spring Cloud Alibaba | Dubbo 与 Spring Cloud 完美结合
    Spring Boot (二):模版引擎 Thymeleaf 渲染 Web 页面
    可以穿梭时空的实时计算框架——Flink对时间的处理
  • 原文地址:https://www.cnblogs.com/xiu68/p/7988554.html
Copyright © 2020-2023  润新知