Ants(POJ No.1852)git
n只蚂蚁以每秒1cm的速度在长为Lcm的竿子上爬行。当蚂蚁爬到竿子的端点时就会掉落。因为竿子太细,两只蚂蚁相遇时,它们不能交错经过,只能各自反向爬回去。对于每只蚂蚁,咱们知道它距离竿子左端的距离xi,但不知道它当前的朝向。请计算全部蚂蚁落下竿子所需的最短期和最长时间。github
限制条件算法
1 ≤ L ≤ 106ide
1 ≤ n ≤ 106函数
0 ≤ xi ≤ Lspa
输入code
输出xml
根据题目描述,咱们不知道蚂蚁的初始朝向,因此两种都有可能。此时,咱们能够先固定第0个蚂蚁的方向,而后再处理其余的蚂蚁。这是一个递归的思路,而且每一个蚂蚁有两个选择,一共2^n种状况,计算每一种状况下,全部蚂蚁掉落的时间,选择最短的、最大的则获得答案。这个题目的时间复杂度是指数级的。真真有些高了,那么如何改进呢?blog
咱们在摘要中说道,这个题目实际上是考察你们想象力的。想象力在哪里呢?咱们首先来看看最短期的状况,直觉上来说,全部的蚂蚁都超最近的一端走,是须要最短期的。那么这时,会不会发生碰撞呢?显然是不能的,A和B是两只不一样的蚂蚁。递归
… | B | A | … |
假设A到左端近,距离为LA<L/2。B到右端近,距离为LB<L/2。LA+LB<L。但从上表看,LA+LB显然要大于L。
下面考虑最长时间的状况,也是该发挥想象力的地方。当两只蚂蚁相遇的时候,原本是要调头爬回去的。这与直接交错走过去有什么不一样呢?蚂蚁的速度是同样的。你们能够举几个具体的例子,看看这两种状况,差异在哪里。例如:
B | A |
这并非巧合。能够认为是一样的状况。主要的缘由就是蚂蚁的速度是相同的,能够认为是独立的。这样,求全部蚂蚁都掉落的最长时间,就是找离某一端距离最长的蚂蚁,而后向着这一端走,所须要的时间。算法的时间复杂度为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); }