ACM 蚂蚁问题

原题

Ants(POJ No.1852)git

n只蚂蚁以每秒1cm的速度在长为Lcm的竿子上爬行。当蚂蚁爬到竿子的端点时就会掉落。因为竿子太细,两只蚂蚁相遇时,它们不能交错经过,只能各自反向爬回去。对于每只蚂蚁,咱们知道它距离竿子左端的距离xi,但不知道它当前的朝向。请计算全部蚂蚁落下竿子所需的最短期和最长时间。github


限制条件算法

1 ≤ L ≤ 106ide

1 ≤ n ≤ 106函数

0 ≤ xi ≤ Lspa

输入code

 
  1. L = 10 
  2. n = 3 
  3. x = {2, 6, 7} 

输出xml

 
  1. min = 4(左、右、右)  
  2. max = 8(右、右、右) 


分析

根据题目描述,咱们不知道蚂蚁的初始朝向,因此两种都有可能。此时,咱们能够先固定第0个蚂蚁的方向,而后再处理其余的蚂蚁。这是一个递归的思路,而且每一个蚂蚁有两个选择,一共2^n种状况,计算每一种状况下,全部蚂蚁掉落的时间,选择最短的、最大的则获得答案。这个题目的时间复杂度是指数级的。真真有些高了,那么如何改进呢?blog

咱们在摘要中说道,这个题目实际上是考察你们想象力的。想象力在哪里呢?咱们首先来看看最短期的状况,直觉上来说,全部的蚂蚁都超最近的一端走,是须要最短期的。那么这时,会不会发生碰撞呢?显然是不能的,A和B是两只不一样的蚂蚁。递归

B A    

假设A到左端近,距离为LA<L/2。B到右端近,距离为LB<L/2。LA+LB<L。但从上表看,LA+LB显然要大于L。

下面考虑最长时间的状况,也是该发挥想象力的地方。当两只蚂蚁相遇的时候,原本是要调头爬回去的。这与直接交错走过去有什么不一样呢?蚂蚁的速度是同样的。你们能够举几个具体的例子,看看这两种状况,差异在哪里。例如:

  B   A    
  • 当相遇调头时,A和B都调下来的最长时间是4秒。A向左一格,而后调头向右三格
  • 当相遇交错走过期,A向左走三格掉落,B向右走四格掉落。则最长时间为4秒。

这并非巧合。能够认为是一样的状况。主要的缘由就是蚂蚁的速度是相同的,能够认为是独立的。这样,求全部蚂蚁都掉落的最长时间,就是找离某一端距离最长的蚂蚁,而后向着这一端走,所须要的时间。算法的时间复杂度为O(n)。

后面求最长时间的关键就是,发挥想象,找到调头和交错走过其实是同样的。就搞定了。

【分析完毕】


首先很容易想到一个穷竭搜索 算法,即枚举全部蚂蚁的初始朝向的组合,这能够利用递归函数实现(详见2.1节)。

每只蚂蚁的初始朝向都有2种可能,n只蚂蚁就是2×2×…×2=2n种。若是n比较小,这个算法仍是可行的,但指数函数随着n的增加会急剧增加。

2n增加的趋势

n
1
5
10
20
 
30
100
10000
1000000
2 n
2
32
1024
1048576
 
109
1030
103010
10301030

穷竭搜索的运行时间也随之急剧增加。通常把指数阶的运行时间叫作指数时间。指数时间的算法没法处理稍大规模的输入。

接下来,让咱们来考虑比穷竭搜索更高效的算法。首先对于最短期,看起来全部蚂蚁都朝向较近的端点走会比较好。事实上,这种状况下不会发生两只蚂蚁相遇的状况,并且也不可能在比此更短的时间内走到竿子的端点。

接下来,为了思考最长时间的状况,让咱们看看蚂蚁相遇时会发生什么。

事实上,能够知道两只蚂蚁相遇后,当它们保持原样交错而过继续前进也不会有任何问题。这样看来,能够认为每只蚂蚁都是独立运动的,因此要求最长时间,只要求蚂蚁到竿子端点的最大距离就行了。

这样,不论最长时间仍是最短期,都只要对每只蚂蚁检查一次就行了,这是O(n)时间的算法。对于限制条件n ≤ 106,这个算法是够用的,因而问题得解。  

这个问题能够说是考察想象力类型问题的经典例子。有不少这样的问题,虽然开始不太明白,但想通以后,最后的程序倒是出乎意料地简单。

// 输入  
int L, n;  
int x[MAX_N];  
 
void solve() {  
  // 计算最短期  
  int minT = 0;  
  for (int i = 0; i < n; i++) {  
    minT = max(minT, min(x[i], L - x[i]));  
  }  
    
  // 计算最长时间  
  int maxT = 0;  
  for (int i = 0; i < n; i++) {  
    maxmaxT = max(maxT, max(x[i], L - x[i]));  
  }  
    
  printf("%d %d\n", minT, maxT);  
}