golang刷leetcode 技巧(11) 课程表 III

2022-08-02 18:39:21 浏览数 (2)

这里有 n 门不同的在线课程,他们按从 1 到 n 编号。每一门课程有一定的持续上课时间(课程时间)t 以及关闭时间第 d 天。一门课要持续学习 t 天直到第 d 天时要完成,你将会从第 1 天开始。

给出 n 个在线课程用 (t, d) 对表示。你的任务是找出最多可以修几门课。

示例:

输入: [[100, 200], [200, 1300], [1000, 1250], [2000, 3200]]

输出: 3

解释:

这里一共有 4 门课程, 但是你最多可以修 3 门:

首先, 修第一门课时, 它要耗费 100 天,你会在第 100 天完成, 在第 101 天准备下门课。

第二, 修第三门课时, 它会耗费 1000 天,所以你将在第 1100 天的时候完成它, 以及在第 1101 天开始准备下门课程。

第三, 修第二门课时, 它会耗时 200 天,所以你将会在第 1300 天时完成它。

第四门课现在不能修,因为你将会在第 3300 天完成它,这已经超出了关闭日期。

提示:

整数 1 <= d, t, n <= 10,000 。

你不能同时修两门课程。

解题思路:

我们首先可以发现,如果两门课 (t1, d1) 和 (t2, d2) 满足 d1 <= d2,即后者的结束时间不晚于前者,那么我们先学习前者再学习后者总是最优的。因为如果先学习前者,即需要满足 x t1 <= d1 且 x t1 t2 <= d2;如果先学习后者,则需要满足 x t2 <= d2 且 x t1 t2 <= d1。如果后者中的 x t1 t2 <= d1 成立,那么显然有 x t1 <= d1 且 x t1 t2 <= d1 <= d2,即前者一定成立;反之如果 x = 0, (t1, d1) = (2, 3), (t2, d2) = (5, 100),那么前者成立且后者不成立。因此先学习前者再学习后者总是最优的。

基于上面的结论,我们可以将课程按照完成时间 d 递增排序。假设我们在前 i 门课中最多可以选取 k 门课,并且这 k 门课的总时长最短(称为最优方案),那么有下面的不等式:

t1 t2 <= d2

t1 t2 t3 <= d3

...

t1 t2 ... tk <= dk

此时我们需要判断第 i 1 门课 (t0, d0) 是否可选。如果选取的 k 门课的总时长 t 与 t0 之和小于等于 d0,即

t1 t2 ... tk t0 <= d0

那么 (t0, d0) 一定可选,此时前 i 1 门课的最优方案是选取 t1, t2, ..., tk, t0 这 k 1 门课。可以使用反证法来证明,假设可以选取超过 k 1 门课,或者选取 k 1 门课且总时长小于 t1 t2 ... tk t0,那么我们去除 (t0, d0) 这门课,剩余的选取方案一定满足条件,且优于选取 t1, t2, ..., tk 的方案,与之间的假设 t1, t2, ..., tk 是最优方案相矛盾。

如果上述不等式不满足,那么我们找出 t1, t2, ..., tk 中时间最长的那一门课 tj,如果 tj > t0,那么我们可以将 tj 用 t0 代替,即 t1, t2, ..., tj-1, tj 1, ..., tk, t0 是一种最优方案。这里同样可以使用反证法来证明。如果 tj <= t0,那么最优方案仍然为 t1, t2, ..., tk。

因此我们依次遍历每一门课程,通过上面的方法,就可以得到最优方案。我们就可以通过优先队列在若干个数中选出最大值,在遍历每一门课程 (ti, di) 时,依次进行如下操作:

如果当前优先队列中所有课程的时间之和 t 与 ti 之和小于等于 di,那么就把 (ti, di) 加入优先队列中;

如果当前优先队列中所有课程的时间之和 t 与 ti 之和大于 di,那么找到当前优先队列中课程时间最大的课程 (tj, dj)(即为堆顶),如果 tj > ti,则将它移出优先队列,并把 (ti, di) 加入优先队列中。

在所有的课程都判断完毕后,优先队列中包含的课程数目就代表了最多能选择的课程数目。

代码实现

代码语言:javascript复制
func scheduleCourse(courses [][]int) int {
    sort(courses,0,len(courses)-1)
    //fmt.Println(courses)
    var index []int
    sum:=0
    for i:=0;i<len(courses);i  {
       if sum courses[i][0]<=courses[i][1]{
           sum =courses[i][0]
          index=priorityQueue(courses,index,i)
       }else if len(index)>0 && courses[i][0]<=courses[index[0]][0]{
            sum=sum courses[i][0]-courses[index[0]][0]
            index=index[1:]
            index=priorityQueue(courses,index,i)
       }
       //fmt.Println(index)
    }
    return len(index)
}

func priorityQueue(courses [][]int,index []int,i int)[]int{
     if len(index)==0{
              index=append(index,i)
           }else{
               ind:=-1
               for j:=0;j<len(index);j  {
                   //注意这里是<多个相等,先干掉前面的
                    if courses[index[j]][0]<courses[i][0]{
                        ind=j
                        break;
                    }
                }
                if ind==-1{
                 index=append(index,i)
                }else{
                //fmt.Println(index[:ind:ind],i,index[ind:],"----",index,ind,courses[i])
                index1:=append(index[:ind:ind],i)
                index=append(index1,index[ind:]...)
                }
           }
           return index
}
func sort(courses [][]int,start,end int){
  p:=start
  i:=start
  j:=end
  tmp:=courses[p]
  for i<=j{
      for p<=j && courses[j][1]>=tmp[1]{
          j--
      }
      if p<j {
          courses[p]=courses[j]
          p=j
      }
      if i<=p && courses[i][1]<=tmp[1]{
          i  
      }
      if i<p{
          courses[p]=courses[i]
          p=i
      }
  }
  courses[p]=tmp
  if p>start 1{
      sort(courses,start,p-1)
  }

  if p 1<end{
      sort(courses,p 1,end)
  }
}

/*
[[5,5],[4,6],[2,6]]
[[7,16],[2,3],[3,12],[3,14],[10,19],[10,16],[6,8],[6,11],[3,13],[6,16]]
[[100,2],[32,50]]
[[100,200],[200,1300],[1000,1250],[2000,3200]]
*/
d3

0 人点赞