2009年ACM-ICPC世界总决赛的飞机调度挑战

时间:2009-12-03 20:14:51

标签: algorithm

出于好奇,我正在查看2009 ACM国际大学生程序设计竞赛的问题。这些问题非常有趣。它们可以在http://cm.baylor.edu/resources/pdf/2009Problems.pdf获得。我无法想出一个解决问题1的算法,我将在这里重现。它在办公室引发了热烈的讨论,我们认为我们非常接近答案,但如果有人能找到/制定完整的解决方案(代码不需要),我们真的很感激。

为了您的方便,我会在这里重现问题:

问题1

考虑安排降落在机场的飞机的任务。进入的飞机报告它们的位置,方向和速度,然后控制器必须设计一个着陆时间表,将所有飞机安全地带到地面。通常,连续着陆之间的时间越长,着陆时间表就越“安全”。这个额外的时间让飞行员有机会对不断变化的天气和其他惊喜做出反应。 幸运的是,这个调度任务的一部分可以自动化 - 这就是你进来的地方。你将获得飞机着陆的情景。每架飞机都有一个时间窗口,在此期间它可以安全降落。您必须计算登陆所有尊重这些时间窗的飞机的订单。此外,飞机着陆应尽可能地伸展,以便连续着陆之间的最小时间间隔尽可能大。例如,如果三架飞机在上午10点,上午10点05分和上午10点15分着陆,那么最小的间隙是五分钟,这发生在前两架飞机之间。并非所有间隙都必须相同,但最小间隙应尽可能大。

输入

输入文件包含几个测试用例,包括着陆方案的描述。每个测试用例都以包含单个整数 n (2≤ n ≤8)的行开始,该行是场景中的飞机数量。接下来是 n 行,每行包含两个整数 a i b i ,它给出了闭合间隔的开始和结束[ a i b i ] 飞机可以安全着陆。数字 a i b i 以分钟为单位指定并满足0≤ a b i ≤1440。 输入以包含单个整数零的行终止。

输出

对于输入中的每个测试用例,打印其案例编号(从1开始),然后是连续着陆之间可达到的最小时间间隔。打印时间分为分钟和秒,四舍五入到最接近的秒。遵循样本输出的格式。

示例输入

3
0 10
5 15
10 15
2
0 10
10 20
0

示例输出

Case 1: 7:30
Case 2: 20:00

4 个答案:

答案 0 :(得分:2)

我将给出算法草图。

首先你binary search通过答案(航班之间的最短间隔)。为此,对于每个选定的时间间隔 T ,您必须能够检查是否可以实现它。如果有可能实现 T ,那么你试着把它做得更小,如果不是 - 把它做得更大。

要检查是否可以实现 T ,请尝试所有n!飞机可能着陆的命令(8!足够小,以便这个算法及时工作)。对于每个排列P1 ... Pn,您尝试在greedy manner中分配时间:

int land = a[0];
for (int i = 1; i < n; i++) {
    land = max(a[i], land + **T**);
    if (land > b[i]) return "CAN NOT ACHIEVE INTERVAL T";
}
return "CAN ACHIEVE";

答案 1 :(得分:1)

这个优化问题可以通过线性编程http://en.wikipedia.org/wiki/Linear_programming

来解决

答案 2 :(得分:0)

我会做这样的事情:

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

typedef uint MASK;
#define INPUT_SCALE 60
#define MAX_TIME (1440 * 60)


void readPlaneData(int& endTime, MASK landingMask[MAX_TIME], int index)
{
    char buf[128];
    gets(buf);
    int start, end;
    sscanf(buf, "%d %d", &start, &end);

    for(int i=start * INPUT_SCALE; i<=end * INPUT_SCALE; i++)
        landingMask[i] |= 1 << index;

    if(end * INPUT_SCALE > endTime)
        endTime = end * INPUT_SCALE;
}


int findNextLandingForPlane(MASK landingMask[MAX_TIME], int start, int index)
{
    while(start < MAX_TIME)
    {
        if(landingMask[start] & (1 << index))
            return start;
        start++;
    }

    return -1;
}


bool canLandPlanes(int minTime, MASK landingMask[MAX_TIME], int planeCount)
{
    int next = 0;
    for(int i=0; i<planeCount; i++)
    {
        int nextForPlane = findNextLandingForPlane(landingMask, next, i);
        if(nextForPlane == -1)
            return false;

        next = nextForPlane + minTime;
    }

    return true;
}


int main(int argc, char* argv[])
{
    while(true)
    {
        char buf[128];
        gets(buf);
        int count = atoi(buf);
        if(count == 0)
            break;

        MASK landingMask[MAX_TIME];
        memset(landingMask, 0, sizeof(landingMask));

        int endTime = 0;
        for(int i=0; i<count; i++)
            readPlaneData(endTime, landingMask, i);

        while((endTime > 0) && !canLandPlanes(endTime, landingMask, count))
            endTime--;

        printf("%d:%02d\n", endTime / 60, endTime % 60);
    }
}

答案 3 :(得分:0)

这是一些暴力破解解决方案的Ruby代码。请注意,test_case_one实际上失败,因为我已经注释掉了使用这个代码的代码(而不是整整几分钟)。

蛮力战略是对飞机降落的所有序列进行置换。对于每个着陆序列,创建所有可能着陆时间的乘积。整整几分钟都很好,几秒钟就很残酷。

但当然是过早优化,邪恶等等,所以这是第一步:

require 'test/unit'

class SampleTests < Test::Unit::TestCase
  def test_case_one
    problem = Problem.new
    problem.add_plane(Plane.new(0, 10))
    problem.add_plane(Plane.new(5, 15))
    problem.add_plane(Plane.new(10, 15))
    problem.solve()
    minimum_gap = problem.minimum_gap()
    assert_equal(7.5, minimum_gap)
  end

  def test_case_two
    problem = Problem.new
    problem.add_plane(Plane.new(0,10))
    problem.add_plane(Plane.new(10, 20))
    problem.solve()
    minimum_gap = problem.minimum_gap()
    assert_equal(20, minimum_gap)
  end

  def test_case_three
    problem = Problem.new
    problem.add_plane(Plane.new(0, 2))
    problem.add_plane(Plane.new(7, 10))
    problem.add_plane(Plane.new(4, 6))
    minimum_gap = problem.minimum_gap()
    assert_equal(5, minimum_gap)
  end

  def test_case_four
    problem = Problem.new
    problem.add_plane(Plane.new(1439, 1440))
    problem.add_plane(Plane.new(1439, 1440))
    problem.add_plane(Plane.new(1439, 1440))
    assert_equal(0, problem.minimum_gap())
  end

  def test_case_five
    problem = Problem.new
    problem.add_plane(Plane.new(0, 10))
    problem.add_plane(Plane.new(1, 2))
    assert_equal(9, problem.minimum_gap())
  end

  def test_case_six
    problem = Problem.new
    problem.add_plane(Plane.new(8, 9))
    problem.add_plane(Plane.new(0, 10))
    assert_equal(9, problem.minimum_gap())
  end
end

class Plane
  def initialize(min, max)
    @ts = Array.new
    #This is a cheat to prevent combinatorial explosion. Just ignore 60 seconds in a minute!
    #min = min * 60
    #max = max * 60
    min.upto(max) { | t | @ts << t}
  end

  #Array of times at which the plane might land. 
  def times
    return @ts
  end
end

#from 'permutation' gem
class Array
  def permute(prefixed=[])
      if (length < 2)
          # there are no elements left to permute
          yield(prefixed + self)
      else
          # recursively permute the remaining elements
          each_with_index do |e, i|
              (self[0,i]+self[(i+1)..-1]).permute(prefixed+[e]) { |a| yield a }
          end
      end
  end
end


class Problem
  def initialize
    @solved = false
    @maximum_gap = 0
    @planes = Array.new
  end

  def add_plane(plane)
    @planes << plane
  end

  #given a particular landing schedule, what's the minimum gap?
  #A: Sort schedule and spin through it, looking for the min diff

  #Note that this will return 0 for invalid schedules (planes landing simultaneously)
  def gap_for(schedule)
    schedule.sort!
    min_gap = 1440
    0.upto(schedule.length - 2) { | i |
      gap = schedule[i + 1] - schedule[i]
      if gap < min_gap
        min_gap = gap
      end 
    }
    return min_gap
  end

  #Brute-force strategy
  #Get every possible plane sequence (permute)
  #Get every possible schedule for that sequence (brute_force_schedule)
  #Check that schedule
  def solve
    @planes.permute { | sequence |
        schedules = brute_force_schedule(sequence)
        schedules.each { | schedule | 
          schedule.flatten!
          gap = gap_for(schedule)
          if gap > @maximum_gap
              #puts "Found a new one: #{schedule.inspect}"
              @maximum_gap = gap
          end
        }
    }
  end

  #The list of all possible schedules associated with an array of planes
  def brute_force_schedule(planes)
    head = planes[0]
    tail = planes[1..-1]
    if tail.empty?
      #Last element, return the times
      return head.times.to_a
    else
      #Recurse and combine (product) 
      return head.times.to_a.product(brute_force_schedule(tail))
    end
  end


  def minimum_gap
    unless @solved
      solve
    end
    return @maximum_gap
  end
end