N个重叠会议日程表的最佳房间数和大小

时间:2014-07-09 15:26:05

标签: algorithm scheduling dynamic-programming intervals greedy

我遇到了这个问题,我不确定我的解决方案是否是最优的。

问题

给定N加权(Wi)和可能重叠的间隔(代表会议日程),找到最小数量"&"会议室开展所有会议所需的能力。

实施例

|---10------|. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .|---------8---------|

        |------8-----|          |----------10-----------|

                  |--------6-------|

对于上述时间表,我们需要两个10和10个容量的会议室。 (我是对的吗?)

我的解决方案

如果我们有一个容量大于所需容量的会议室使用它,如果没有符合标准的会议室,建立一个新的房间或增加现有的房间,请从一个房间,并从左侧移动间隔有新容量的房间。

示例:

10开始 - {10}

8开始 - {10,8}

10月底 - {10-free,8}

6的开始 - {10,8}

8结束 - {10,8-free}

10的开始= {10,8 + = 2}或{10,10}

依旧.....

这基本上是贪婪的..

  • 有人可以证明这不是最佳的吗?
  • 如果这不是最佳的,那么解决方案是什么? DP?

6 个答案:

答案 0 :(得分:6)

我认为这个问题相当于“铁路/公交车站所需的最低平台数”问题。

本文http://www.geeksforgeeks.org/minimum-number-platforms-required-railwaybus-station/解释了如何处理它。

答案 1 :(得分:4)

直觉

我会试一试。天真的方法是列举所有可能的解决方案并选择最好的解决方案。考虑到这一点,找到可以容纳k次会议的n个会议室等同于找到k分段的n点。 OP示例中2会议的5分区方式示例为[ 0,2,4 ][ 1,3 ]

|---0------|                     |---------4---------|

   |------1-----|          |----------3-----------|

             |--------2-------|

因此,基本思想是枚举k个会议的所有n - 路分区,其约束条件是两个重叠的会议不能属于同一个群集。例如,[ 0,1,2 ][ 3,4 ]不是有效分区,因为会议[ 0,1,2 ]无法在会议室中进行;会议同样适用[ 3,4 ]。幸运的是,使用递归方法时,约束很容易实现。

算法

使用Python,它看起来像这样:

def kWay( A, k, overlap ) :
    """
    A = list of meeting IDs, k = number of rooms, 
    overlap[ meeting ID m ] = set of meetings overlapping with m 
    """
    if k == 1 : # only 1 room: all meetings go there
        yield [ A[:] ]
    elif k == len(A) : # n rooms and n meetings: put 1 meeting per room
        yield [ [a] for a in A ]
    else :
        for partition in kWay( A[1:], k, overlap ) : # add new meeting to one existing room
            for i, ci in enumerate( partition ) :
                isCompatible = all( A[0] not in overlap[x] for x in ci ) # avoid 2 overlapping meetings in the same room
                res = partition[:i] + [ ci + [ A[0] ] ] + partition[ i+1: ]
                if isCompatible :
                    yield res
        for partition in kWay( A[1:], k-1, overlap ) : # add new meeting to a new room
            isValid = ( set(A[1:]) & set.union( * ( overlap[a] for a in A[ 1: ] ) ) == set() ) # avoid 2 overlapping meetings in the same room
            if (k-1>1) or ( k-1==1 and isValid ) :
                yield partition + [ [ A[0] ] ]

这看起来有点复杂但实际上很简单,当你意识到它只是k方式分区+ 2个额外行的递归算法,以保证我们只考虑有效分区。

OP示例的解决方案

好的,现在让我们使用OP示例准备输入数据:

import collections

n = 5
k = 2
#
A = range(n)
# prepare overlap dictionary
pairs = [ (0,1), (1,2), (2,3), (3,4) ] # overlapping meetings
size = dict( ( (0,10), (1,8), (2,6) , (3,10), (4,8) ) )

overlap = collections.defaultdict(set)
for (i,j) in pairs :
    overlap[i].add(j)
    overlap[j].add(i)

defaultdict(<type 'set'>, {0: set([1]), 1: set([0, 2]), 2: set([1, 3]), 3: set([2, 4]), 4: set([3])})
{0: 10, 1: 8, 2: 6, 3: 10, 4: 8}

现在我们只是遍历有效的2路分区并打印房间大小。只有一个有效的分区,所以这是我们的解决方案:

for partition in kWay( A, k, overlap ) :
    print partition, [ max( size[x] for x in c ) for c in partition ]
[[3, 1], [4, 2, 0]] [10, 10]

好的,所以会议1,3会占用10个会议室,会议0,2,4会进入10的会议室。

稍微复杂的例子

但是只有一个有效的2 - 路分区,所以当然这也是最佳解决方案。多么无聊!让我们为OP示例添加一个新会议5和一个新会议室,以使其更有趣:

|---0------|            |---5---|        |---------4---------|

   |------1-----|          |----------3-----------|

             |--------2-------|

对应的输入数据:

n = 6
k = 3
#
A = range(n)
pairs = [ (0,1), (1,2), (2,3), (3,4), (5,2), (5,3) ] # overlapping meetings
size = dict( ( (0,10), (1,8), (2,6) , (3,10), (4,8), (5,2) ) )

overlap = collections.defaultdict(set)
for (i,j) in pairs :
    overlap[i].add(j)
    overlap[j].add(i)

defaultdict(<type 'set'>, {0: set([1]), 1: set([0, 2]), 2: set([1, 3, 5]), 3: set([2, 4, 5]), 4: set([3]), 5: set([2, 3])})
{0: 10, 1: 8, 2: 6, 3: 10, 4: 8, 5: 2}

结果:

for partition in kWay( A, k, overlap ) :
    print partition, [ max( size[x] for x in c ) for c in partition ]
[[3, 1], [4, 2, 0], [5]] [10, 10, 2]
[[3, 1], [4, 2], [5, 0]] [10, 8, 10]
[[3, 0], [4, 2], [5, 1]] [10, 8, 8]
[[3], [4, 2, 0], [5, 1]] [10, 10, 8]
[[4, 5, 1], [3, 0], [2]] [8, 10, 6]
[[4, 5, 1], [3], [2, 0]] [8, 10, 10]
[[4, 5, 0], [3, 1], [2]] [10, 10, 6]
[[4, 5], [3, 1], [2, 0]] [8, 10, 10]

最佳3 - 方式分区为[[3, 1], [4, 2, 0], [5]],最佳房间大小为[10, 10, 2]。您还可以直接获得所有房间的最小尺寸:

min( sum( [ max( size[x] for x in c ) for c in partition ] ) for partition in kWay( A, k, overlap ) )

22

答案 2 :(得分:1)

考虑这种情况:

(m1) |-3-|
(m2)  |--2--|
(m3)   |--1--|
(m4)      |-1-|
(m5)       |-2-|

您的解决方案将继续:

  1. {3}(创建第一个房间)
  2. {3,2}(同时召开两次会议,需要第二个会议室)
  3. {3,2,1}(同时召开三次会议,需要第三个会议室)
  4. {3,2,1}(m1结束,所以m4进入3个房间)
  5. {3,2,1,2}(同时召开四次会议,需要第四个会议室,创建与最新会议相同规模的会议室)
  6. 此解决方案的累积容量为8。

    现在考虑这个解决方案:{3,2,1,1}。它的累积容量为7 在上面的步骤(4),m4将进入未占用的1个房间,3个房间仍然是打开的。因此,这就是m5将要去的地方。

    做出的假设

    1. 最佳解决方案首先按房间数排名:它会 拥有最少的房间。第二个标准是它会 具有最低的累积容量:容量的总和 每个房间。
    2. 当你的解决方案被认定为贪婪时 创建一个房间,你将创建一个大小的房间 评价。
    3. 无论大小如何,两次会议不能同时在同一个房间。
    4. 算法更改

      更新:我刚刚意识到,即使进行此更改,创建一个房间仍然可以导致次优解决方案。原因是人们可以在创建新房间之前调整现有房间的大小。

      举个例子,假设我们在四个房间里举行了四次会议。

      • m1(4号)是4室
      • m2(2号)是4室
      • m3(尺寸1)在2个房间
      • m4(尺寸1)在2个房间

      我们寻求添加m5(5号)。我提议的算法更改将创建一个新的5个房间,累积容量增加5。但是,我们可以将m2的房间调整为5个房间,让m5去那里,并为m2建造一个2号房间。这只会增加2个累积容量。

      有人可能想知道为什么不将m2放入2个房间之一(取代m3)并创建一个新的1个房间。调整房间大小更加困难,因为我们不能保证在需要它的会议开始时会打开房间。添加房间比较容易,因为那个房间总是在那里;它没有被使用,因为我们刚刚在算法的这一步创建了它。

      次优算法更改 如上所述,这被证明是次优的,但我一直在这里,直到我能想到一个更好的选择。

      要考虑上述情况,您需要在创建新房间时进行额外的工作:

      1. 查找当前有效的所有会议的列表(包括您当前正在评估的会议)。
      2. 从最大的会议开始,将每个会议分配到一个房间。
      3. 当您到达无法分配的会议时,您必须创建该会议室的大小。
      4. 因此,在上面的示例中,当需要创建新房间时,这种改变在步骤5起作用。上面的步骤说明:

        1. 目前有效的所有会议:{m2,m3,m4,m5}。根据记录,目前的房间是{3,2,1}
        2. 从最大的开始,将每个会议分配到一个房间{m2进入3个房间,m5进入2个房间,m3进入1个房间}
        3. m4没有房间。因此,我们必须为它创造一个空间。 m4的尺寸为1,所以新房间的尺寸也是1。

答案 3 :(得分:1)

要查找举行所有会议所需的会议室的最低数量和容量, 您首先需要将这些会议安排到房间(具有最小化房间容量的分数功能)。该调度(类似于course scheduling)是NP完全的或NP难的。这意味着你的问题也是如此。

反过来,这意味着没有已知的算法可以解决您的问题。贪心算法(包括你的例子)不会始终是最优的(或者如果你有更多的约束,甚至不是最优的) - 但至少它们会缩放:)为了获得更好的结果(如果需要),请研究优化算法,例如metaheuristics。

答案 4 :(得分:-1)

{
        "id": "GDT Expanded",
        "name": "GDT Expanded",
        "version": "0.1",
        "author": "Steven_Rochfort",
        "url": "http://sites.simbla.com/fb3f8974-2f77-9836-04e4-d36177c9da09/Home",
        "description": "This mod aims to expand your game by adding topics, researches, events etc. **THIS MOD IS AT AN EARLY STAGE, USE AT YOUR OWN RISK**",
        "main": "./source.js",
        "dependencies": {
            "gdt-modAPI": "0.1.x"
        },
        "image": "Icons/GDTE.png"
    }

答案 5 :(得分:-1)

以下是我的解决方案。

class Meeting{

    LocalTime start;
    LocalTime end;
    Meeting(LocalTime start, LocalTime end){
        this.start = start;
        this.end = end;
    }
}


public static int meeingRoom(List<Meeting> list){


    //use queue structure to store the room in use
    Queue<Meeting> rooms = new LinkedList<Meeting>();
    rooms.add(list.get(0)); 

    for(int i = 1; i< list.size(); i++){ 

         Meeting current = list.get(i); 

         //max: keep the max of ever occupied
         //occupied: so far occupied room

         int  max = 1, occupied = 1; 
         List<Meeting> rooms = new ArrayList<Meeting>();
         rooms.add(list.get(0));

         for(int i = 1; i< list.size(); i++){ 

            Meeting current = list.get(i); 
            int roomSize = rooms.size();

            //check all previous rooms to release finish room
            for(int j = 0; j < roomSize; j++){ 
               if(j >= rooms.size()) break;

               Meeting previous = rooms.get(j);

               if(current.start.compareTo(previous.end) >= 0){ 
                   rooms.remove(j);
               } 

           rooms.add(current); 

           //when all the rooms once occupied, all remove
           //reset the occupied
           if(rooms.size() == 1 ){ 
               max = Math.max(occupied, max);
               occupied = 1; 
           }else{ 
              occupied = Math.max(occupied, rooms.size()); 
           };


    }

    //the last time added room hasn't been check
    return Math.max(occupied, max);
}