【问题】n只蚂蚁以每秒1cm的速度在长为Lcm上的杆子上爬行。当蚂蚁爬到杆子的两个端点的时候,就会从杆子上掉落下来。规定两只蚂蚁相遇的时候,不能交叉通过,只能各自掉头爬回去。假设对于蚂蚁 i 来说,它的初始位置与杆子左端的距离是Xi,但是不知道它的朝向。请计算所有蚂蚁掉落杆子需要的最短时间和最长时间。
限制条件是
1<><>
1<><>
0<>Xi<>
以上是一道经典的POJ题目。假设输入为
L=10
n=3
X={2,6,7}
输出为
min=4(此时蚂蚁的朝向可以依次是左,右,右)
max=8(此时蚂蚁的朝向可以依次是右,右,右)
这个题目的复杂性在于我们不知道蚂蚁的初始朝向。很容易想到的一个直观的方法是穷举法,即列举出蚂蚁所有初始朝向的组合,然后计算出每种蚂蚁组合掉落时间,找出其中最短的时间和最长的时间。
下面列出了所有朝向组合以及算出对应的蚂蚁掉落时间(见下图):
我们来看看这种算法的复杂性。由于每种蚂蚁开始的朝向都有2种可能,n只蚂蚁就有2*2*2*...*2=2**n(2的n次方)可能性。这是一个指数函数。在n比较小的时候,还是可行的,但是随着n的增长,复杂性会急剧增长,参考下面的表格。
因此这种算法在n较大的情况下不可行,我们必须另想别法。
首先,考虑最短时间。如果所有蚂蚁都向离它较近的那个端点方向爬行,那么蚂蚁就不会相遇,而且不可能在比它更短的时间内走完,这种情形肯定是最短时间。此时只要找出每只蚂蚁到端点较近的距离,然后从中找出一个最大值就可以。
再考虑最长的时间,是否就是所有蚂蚁向离它较远的那个端点方向爬行就可以了呢?还不能肯定,因为此时需要考虑蚂蚁相遇掉头的情况。
让我们看看两只蚂蚁相遇的情形:
假设蚂蚁1(朝右)与蚂蚁2(朝左)在距离杆子左端的X处相遇,那么蚂蚁1将掉头向左爬回去,而蚂蚁2也将掉头向右爬回去。
之后它们不再相遇,一直到两只蚂蚁全部掉下杆子为止。此时蚂蚁1在X秒后在杆子的左端掉下杆子,而蚂蚁2在杆子的右端掉下花费时间为(10-X)秒。因此它们全部掉落杆子的时间是X和10-X中较大的值:max(X,10-X)。
事实上,如果我们假设两只蚂蚁相遇后,它们保持原来方向继续向前爬行,两只蚂蚁全部掉落杆子的时间不会变化。
上图中,两只蚂蚁相遇后,蚂蚁1保持向右爬行,到它掉落杆子的右端时间为(10-X)秒,而蚂蚁2保持向左爬去,到它掉落杆子左端的时间是X秒。于是它们全部掉落的时间还是X和10-X中较大的值:max(X,10-X)。
两只蚂蚁的情形可以推广到n只蚂蚁的情形。这样,我们可以假设每只蚂蚁都是独立运动的,并且每只蚂蚁都向离它较远的杆子端点爬去,从中找出最长时间,它就是所有蚂蚁掉落杆子的最长时间。因此通过找出所有蚂蚁中离杆子端点最大距离可以计算出最长时间。
对所有蚂蚁检查一次它到两端的距离即可,也就是算法的复杂度是O(n)。可以满足输入限制条件n<>
下面是对应的一段C/C++程序的类代码。
int L, n ; //L是杆子长度,n是蚂蚁数目
int x[LIMIT];//数组x记录每个蚂蚁离杆子左侧的距离,LIMIT是最大的蚂蚁数
int max(int a,int b) //max函数返回两数中的较大值
{
int result;
result = b;
if (a>b) result = a;
return result;
}
int min(int a,int b) //min函数返回两数中的较小值
{
int result;
result = b;
if (a
return result;
}
void calculateTime()
{
int minTime = 0; //minTime是最短时间
int maxTime = 0; //maxTime是最长时间
for (int i=0;i<>
{
minTime = max (minTime, min(x[i], L-x[i]));
maxTime = max (maxTime, max(x[i], L-x[i]));
}
printf('%d %d\n',minTime,maxTime);
}