ACM 蚂蚁问题

原题

Ants(POJ No.1852)

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


限制条件

1 ≤ L ≤ 106

1 ≤ n ≤ 106

0 ≤ xi ≤ L

输入

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

输出

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


分析

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

我们在摘要中说道,这个题目其实是考察大家想象力的。想象力在哪里呢?我们首先来看看最短时间的情况,直觉上来讲,所有的蚂蚁都超最近的一端走,是需要最短时间的。那么这时,会不会发生碰撞呢?显然是不能的,A和B是两只不同的蚂蚁。

B A    

假设A到左端近,距离为LA

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

  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

穷竭搜索的运行时间也随之急剧增长。一般把指数阶的运行时间叫做指数时间。指数时间的算法无法处理稍大规模的输入。

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

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

ACM 蚂蚁问题_第1张图片

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

这样,不论最长时间还是最短时间,都只要对每只蚂蚁检查一次就好了,这是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);  
} 


你可能感兴趣的:(C/C++,ACM)