16.1-4

  问题的描述

有n个活动,对于其中的每个活动Ai均有一个开始时间Si和结束时间Fi表示该活动的举办时间是[Si, Fi), 其中0 <= Si < Fi < MAX。现在我们希望使用尽可能少的教室来调度所有的活动。注意每个活动在其举办时间内都独占公共的资源(比如教室等),所以一个教室同一时间只能有一个活动。

      为了与活动选择问题区别,我们将该问题成为活动全选择问题。

      等价的描述一:活动全选择问题模型可以转化成一个区间图,其顶点为活动,如果两个活动不兼容则在其对应的顶点上连一条边。为使任两个相邻结点的颜色互不相同,所需的最少颜色数对应于找出调度给定的所有活动所需的最少教室数。这个问题就是经典的区间图着色问题(interval-graph-coloring-problem)。

      等价的描述二:n条线段,每条线段都有起点S和终点F。现在将这n条线段分为尽量少的组,且每组的线段段互不重叠。求这个组数。

      解题算法

 O(n^2)解法:
  按e[i]进行排序,然后按CLRS16.1节所用的greedy algorithm每次选取最多活动给一个活动地点,即首先调用这个函数,得到可以兼容的最大活动数,然后再在余下的活动中再次调用这个函数,直至活动为0
    排序O(n*logn);
    每次greedy algorithm O(n),最坏情况下会使用n次greddy algorithm, O(n^2);
  故总的时间复杂度O(n^2).

O(n*logn)解法:
 

1.对于所有活动的时间点按升序进行排序(n个活动,就有2n个时间点),记录每个时间是起始的还是终止的,在排序的时候,对于值相同的时间点,如果是终止时间点的话,就排在前面。
2.最开始,选择第一个起始时间点,把它对应的活动放入一个教室,同时记录这个起始时间点对应的终止时间点。
3.接着按序选择第i个起始时间点(只选择起始时间点),对于第i个起始时间点,比较它和已有教室中的活动的终止时间点,若大于某个终止时间点,则直接将第i个起始时间点对应的活动放进相应的教室,否则新开辟一个教室来放入这个活动。 见代码清单-2

对于区间图着色(interval-graph coloring)问题,先在一个集合中放入一个点,然后把不与这个点相邻的所有元素放入这个集合,对于剩下的点,重复前面的动作即可,依此循环,直至没有点可选。最后,有多少个集合就是多少种颜色,集合中的元素用相同的色渲染。

#include <stdio.h>
#include <stdlib.h>


//每个时间点;是起始时间,还是终止时间;及其对应的结束时间
typedef struct point_t
{
  int time;
  int is_start;
  int end_time;//若is_start为1,end_time写对应的时间;若is_start为0,end_time为-1
} point;


//升序排列,若时间相同,则为终止时间的时间点排在前面
int compare(const void* a, const void* b)
{
  if ((*(point*)a).time != (*(point*)b).time)
    return (*(point*)a).time > (*(point*)b).time;
  else 
    return (*(point*)a).is_start < (*(point*)b).is_start;//这里得用小于
}


void process(point* points, const int n)
{
  //排序
  qsort(points, n, sizeof(point), compare);
  //最多n/2个教室
  int classrooms[n/2];
  int count = 0;
  classrooms[count++] = points[0].end_time;
  printf("[%d, %d)在教室%d\n", points[0].time, points[0].end_time, count);
  int i;
  int j;
  for (i = 1; i < n; i++)
  {
    if (points[i].is_start == 1)
    {
      for (j = 0; j < count; j++)
      {
    if (classrooms[j] <= points[i].time)
    {
      classrooms[j] = points[i].end_time;
      printf("[%d, %d)在教室%d\n", points[i].time, points[i].end_time, j+1);
      break;
    }
      }
      if (j == count)
      {
    classrooms[count++] = points[i].end_time;
    printf("[%d, %d)在教室%d\n", points[i].time, points[i].end_time, count);
      }
    }
  }
  printf("总共需要%d个教室.\n", count);
}



int main()
{
  int rows;
  scanf("%d", &rows);
  //2*rows个点
  point* points = (point*)malloc(2*rows*sizeof(point));
  //point p;
  int n = rows;
  //point p;
  int start_time;
  int end_time;
  while (rows--)
  {
    int id = n - rows - 1;
    scanf("%d%d", &start_time, &end_time);
    point p1;
    p1.is_start = 1;
    p1.time = start_time;
    p1.end_time = end_time;
    points[2*id] = p1;    
    
    point p2;
    p2.is_start = 0;
    p2.time = end_time;
    p2.end_time = -1;
    points[2*id + 1] = p2;
  }
  process(points, 2*n);
  free(points);
  return 0;
}
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值