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是两只不同的蚂蚁。

BA  

假设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,这个算法是够用的,于是问题得解。  

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

[cpp]  view plain  copy
 print ?
  1. // 输入    
  2. int L, n;    
  3. int x[MAX_N];    
  4.    
  5. void solve() {    
  6.   // 计算最短时间    
  7.   int minT = 0;    
  8.   for (int i = 0; i < n; i++) {    
  9.     minT = max(minT, min(x[i], L - x[i]));    
  10.   }    
  11.       
  12.   // 计算最长时间    
  13.   int maxT = 0;    
  14.   for (int i = 0; i < n; i++) {    
  15.     maxmaxT = max(maxT, max(x[i], L - x[i]));    
  16.   }    
  17.       
  18.   printf("%d %d\n", minT, maxT);    
  19. }   
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值