之前看有的朋友談百度的一道面試試題-螞蟻問題(有一根27厘米的細木桿,在第3厘米、7厘米、11厘米、17厘米、23厘米這五個位置上各有一只螞蟻。木桿很細,不能同時通過一只螞蟻。開始時,螞蟻的頭朝左還是朝右是任意的,它們只會朝前走或調頭,但不會后退。當任意兩只螞蟻碰頭時,兩只螞蟻會同時調頭朝反方向走。假設螞蟻們每秒鐘可以走一厘米的距離。編寫程序,求所有螞蟻都離開木桿的最小時間和最大時間)。關于這道題目,網上給出了很多的解釋,但從整體來看,基本都是用到了等價置換(等量代換)的思想。要求最小時間,即為“最不容易”先到達兩端的螞蟻以最短的時間到達,所以我們只需找到所有螞蟻中間的一只(共奇數只螞蟻)或兩只(共偶數只螞蟻)到達一端的最短時間。比較麻煩的是求最長時間,有人會覺得當有很多只螞蟻時,中間的螞蟻們相互碰撞的次數多些會增加時間,感覺上比較復雜,可如果我們用等量代換的思想來解釋就比較容易。假設中間的任意兩只相鄰螞蟻即將發生碰撞,如:A -> <-B,當A,B發生碰撞后,便有<-A B->。A,B反向相當于<-B A -> ,即二者繼續向著原來的方向前進,對于任意相鄰的發生碰撞的螞蟻都適用,所以只需求最兩端的兩只螞蟻距離兩端的最遠距離。由以上分析可知,如果出這樣的問題,我們可以不用通過程序便能說出結果:5個點,中間螞蟻的位置為11,即0-11-27,顯然最小為11,最兩端螞蟻,0-3-27,最大為24,0-23-27,最大為23,所以最大為24。對于這個題,給出如下Java代碼(隨便寫了幾句,不符合面向對象思想)。
public class Ant {
public static void main(String[] args){
int length=27,points=5,min=0,max=0,temp_min=0,temp_max=0;
int[] pos={3,7,11,17,23};
for(int i: pos){
temp_min=i>length-i?length-i:i;
temp_max=i<length-i?length-i:i;
if(temp_min>min)
min=temp_min;
if(temp_max>max)
max=temp_max;
}
System.out.println("最短時間:"+min+" 最長時間:"+max);
}
}
有了如上的想法,我們能做出判斷,為什么還要寫代碼呢?其實這個問題出自Waterloo Local Contest Sep.19,2004 準確描述如下:
An army of ants walk on a horizontal pole of length l cm, each with a constant speed of 1 cm/s. When a walking ant reaches an end of the pole, it immediatelly falls off it. When two ants meet they turn back and start walking in opposite directions. We know the original positions of ants on the pole, unfortunately, we do not know the directions in which the ants are walking. Your task is to compute the earliest and the latest possible times needed for all ants to fall off the pole.
The first line of input contains one integer giving the number of cases that follow. The data for each case start with two integer numbers: the length of the pole (in cm) and n, the number of ants residing on the pole. These two numbers are followed by n integers giving the position of each ant on the pole as the distance measured from the left end of the pole, in no particular order. All input integers are not bigger than 1000000 and they are separated by whitespace.

For each case of input, output two numbers separated by a single space. The first number is the earliest possible time when all ants fall off the pole (if the directions of their walks are chosen appropriately) and the second number is the latest possible such time.

Sample Input
2
10 3
2 6 7
214 7
11 12 7 13 176 23 191

Sample Output
4 8
38 207

在這里給出相應的c++代碼:
#include<iostream>
using namespace std;
int main()
{
int cases,l,n,min,max,temp_min,temp_max,pos;
cin>>cases;
while(cases--)
{
cin>>l>>n;
min=0;
max=0;
while(n--)
{
cin>>pos;
temp_min=pos>l-pos?l-pos:pos;
temp_max=pos<l-pos?l-pos:pos;
if(temp_min>min)
min=temp_min;
if(temp_max>max)
max=temp_max;
}
cout<<min<<' '<<max<<endl;
}
return 0;
}

























































下面用數學歸納法證明一下這個算法。讓我們先來看最簡單的形式(m=1),有兩只螞蟻的情況,假設細木桿的長度為n,兩只螞蟻的位置分別為p1和p2。并且p2 > p1。其他的條件如原題。下面分別用p1和p2來代表兩只螞蟻。
對于兩只螞蟻,方向組合只有四種,分別如下:
1. p1、p2都向左 最長時間:time(p2)
2. p1、p2都向右 最長時間:time(p1)
3. p1向左、p2向右 最長時間:max(time(p1), time(n - p2))
4. p1向右、p2向左(這種情況會發生碰撞)
最長時間:max(time(n - p1), time(p2))
對于這種情況,實際上p2的時間為time(((p2 - p1) / 2 ) * 2 + n - p2 )= time(n - p1)
p1的時間為time(((p2 - p1) / 2 ) * 2 + p1) = time(p2),
從最終結果看,A- >, < -B 就相當于< -B , A - > 相當于A、B互相穿越而過,并且B只走到A最初的位置,而A也只走到B最初的位置。
從上面四種情況的最長時間可看出,正好將兩種螞蟻p1和p2可能存在的的四種可能:p1、p2、n - p1、n-p2 。我們要做的就是求這四個值的最大值,即max(time(p1)、time(p2)、time(n - p1)、time(n-p2))。 如果用文字來描述的話,就是任意一只螞蟻到達兩端的最長時間就是最終結果。
最簡單的情況已經ok了,現在假設m只螞蟻也成立,那么這m只螞蟻也可以看成是一只螞蟻,而m+1只螞蟻當然就相當于兩只螞蟻了。所以
m = 1 成立
假設m成立
而證明了m+1也成立
所以“任意一只螞蟻到達兩端的最長時間就是最終結果”的結論成立
我認為還有更好的方法,我在原文給出了最長時間的分析。
這里我加上最短時間分析:
其實文中螞蟻相撞兩螞蟻調頭
可以這樣看問題,如果相撞后,桿可以過兩只螞蟻
螞蟻不調頭,即擦肩而過,繼續沿原來方向前進(與調頭是一樣的,只是換了一只螞蟻而已)
這樣問題就簡化了,呵呵。
即所有螞蟻中離桿其中一端最遠的那只螞蟻,即為最長時間。
即27-3=24
同理,最短時間為
所有螞蟻使用時間:A最短是3, B最短是7, C最短11, D最短是10, E最短是4。
所以總的最短時間為11.
不知道小弟的思路是否正確?~~~~
·最長時間:是按照你說的“螞蟻不調頭,即擦肩而過,繼續沿原來方向前進(與調頭是一樣的,只是換了一只螞蟻而已)”這樣,但有兩點說明:(1)通過分析,我們只需判斷最兩端的兩只螞蟻便足夠了,證明見二樓“銀河使者”的解釋。(2)一定要求出4個數進行比較后才可以,假設最左端和最右端的位置為L和R,中長度為T,那么最長的時間為max(max(L,T-L),max(R,T-R))。
·最短時間:通過分析后,也只需看最中間的一個(共奇數螞蟻)或兩個(共偶數螞蟻)螞蟻到達某一端的最短時間,對于奇數:min(L,T-L),對于偶數:max(min(L,T-L),min(R,T-R))。
·通過程序來實現是有必要的,這算是一道模擬題目,可在情況類似的計算應用中會遇到,正如英文描述中可能出現1000000數量級別的,這樣我們便無法直接得出結果。此題能作為面試題,而不是筆試題,就是在考察一種思想,避開“一根不能同時通過兩只螞蟻”的迷惑。
·感謝大家能仔細討論這個問題,寫程序相信每個人都能實現,只是這道題給我們的啟發是非常大的~
至于為什么說27-3是最長,我中間跳躍一些而已。
就是觀察分析了兩端的螞蟻后說的, A離左邊3, E離右邊4。
所以最長就是27-3=24。 上一句分析我省略了而已,SORRY。
最短:
我只是將五個螞蟻的最短時間列出來而已, 最長時間列出來沒意義了。
如果用式子總結便是: 最靠近桿中間點的那個螞蟻的最短時間,即C距離左邊。
@dreamingnest
序列中的元素帶有方向,進行負值部分移動到負值區域,正值部分移動到正值區域時就不再發生碰撞,此時絕對值最小的值決定剩余爬行時間