北京大學複試上機 墜落的螞蟻(牛客網)王道機試指南習題2.11

思路解析:

兩個問題:
1.滿足什麼樣的條件最初中間靜止的螞蟻不會掉下去?
2. 如果會掉下去,那麼會在什麼時候掉下去,數學條件是什麼?
兩個推論:
  • 此題不考慮過程,什麼時刻哪隻螞蟻具體在哪兒由哪個方向到此不作過程分析,只做宏觀的狀態分析,某個時刻哪個位置有一隻螞蟻:
  • 推論一:首先確定:樹枝上最初向左的螞蟻數量和向右的螞蟻數量在整個螞蟻移動過程中是動態平衡的,亦即:任何時候向左(右)的螞蟻數量都等於最初向左(右)移動的螞蟻數量;
    • 原因:每當有兩個螞蟻碰頭時,他們交換了速度,也就是交換了移動方向,也就是向左移動這個狀態不過從一個螞蟻轉移到了另一個螞蟻,另一個螞蟻向右移動的狀態轉移到了前面這個螞蟻頭上,因此,在這個螞蟻移動過程中,總的向左移動的狀態數保持不變,向右移動的狀態數不變;
    • 模型:把樹枝擬化爲數軸,另外,爲了更好地考慮地狀態,對於掉下去的螞蟻,我們把樹枝兩端做個延展,對於到達0或100位置掉下去的螞蟻,我們只做標記上的刪除,不做實際的刪除,也就是掉下去的螞蟻還會繼續沿着數軸的方向移動;
  • 推論二:其次明確:假如某時某刻,左邊已經掉下去了n只螞蟻,右邊掉下去了m只螞蟻,那麼我們可以確定此時左邊掉下去的n只螞蟻,一定是最初時從左邊數的前n只螞蟻,右邊同理,掉下去的m只螞蟻,一定是最初從右邊數的前m只螞蟻;
解決問題
  • 什麼條件下最初中間靜止的螞蟻最後不會掉下去?
    • 最後中間的螞蟻不會掉下去,那麼可假設:左邊掉下去了n只螞蟻,右邊掉下去了m只螞蟻,那麼狀態一定是這樣的:
      在這裏插入圖片描述

    • 由上,可見向左運動的螞蟻有n只,向右運動的螞蟻有m只;根據上面的推論二,從左邊掉下去的n只螞蟻,一定是最初的最左邊的n只螞蟻,右邊同理;根據推論一,整個移動過程螞蟻的移動狀態動態平衡,此時如果我們把從左邊掉下去的螞蟻計作向左移動,右邊掉下去的螞蟻計作向右移動,那麼根據動態平衡,可得:最初向左移動的螞蟻是n只,最初向右移動的螞蟻是m只;

    • 由上可得結論:如果最初向左移動的螞蟻數量=最初中間螞蟻左邊螞蟻的數量,那麼最後可得中間的螞蟻一定不會調出樹枝。反過來講,如果二者不相等,那麼最後左邊掉出去的螞蟻的數量並不等於所有向左移動的螞蟻數量,這顯然不符合推論一的動態平衡;

  • 如果中間的螞蟻會掉下去,會在在什麼時刻,滿足什麼條件?假如根據上述辦法已經確定中間螞蟻一定會掉下去,首先確實初始狀態下向左移動left和向右移動的螞蟻數量right,由此確定如果所有螞蟻掉下去,從左邊掉下去的螞蟻數量(=left)和從右邊掉下去的螞蟻數量(=right),記最初中間螞蟻從左邊數是第xl只螞蟻,從右邊數是第xr只螞蟻;
    • 若xl<left或xr>right,那麼可以判定中間的螞蟻是從左邊掉下去的;
    • 若xl>left或xr<right,那麼可以判定中間的螞蟻是從右邊掉下去的;
      假定從左邊掉出去,由此,只要每一輪移動後都檢查是否有螞蟻從左邊掉出去,那麼從左邊當掉下去的螞蟻爲xl的時刻,即是最初中間靜止的螞蟻掉下樹枝需要花費的時間;

附代碼如下:

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

typedef struct {
	int pos;
	int rate;
}ant_t;

bool cmp(ant_t a, ant_t b) {
	return a.pos < b.pos;
}

int main() {
	int i, N;
	ant_t ant[100];
	int moveL, moveR, left, right;//moveL/moveR表示最初方向向左(右)移動的螞蟻數量
	//left/right表示最初靜止的螞蟻左邊/右邊螞蟻數量
	while (scanf("%d", &N) != EOF) {
		moveL = moveR = 0;
		for (i = 0; i < N; i++) {
			scanf("%d %d", &ant[i].pos, &ant[i].rate);
			if (ant[i].rate == -1) {
				moveL++;
			}
			else if (ant[i].rate == 1) {
				moveR++;
			}
		}
		sort(ant, ant + N, cmp);
		left = 0;
		for (i = 0; i < N; i++) {
			if (ant[i].rate == 0) {
				break;
			}
			left++;
		}
		right = N - left - 1;
		int leftFall = 0, rightFall = 0, t = 0;
		if (left == moveL || right == moveR) {
			printf("Cannot fall!\n");
		}
		else if (left < moveL || right > moveR) {//中間的螞蟻最終會從左邊掉下樹枝
			while (1) {
				for (int j = 0; j < N; j++) {
					if (ant[j].pos == 0) {
						leftFall++;
					}
					ant[j].pos += ant[j].rate;
				}
				if (leftFall == left + 1) {
					printf("%d\n", t);
					break;
				}
				t++;
			}
		}
		else if (left > moveL || right < moveR) {//中間的螞蟻最終會從右邊掉下數值
			while (1) {
				for (int j = 0; j < N; j++) {
					if (ant[j].pos == 100) {
						rightFall++;
					}
					ant[j].pos += ant[j].rate;
				}
				if (rightFall == right + 1) {
					printf("%d\n", t);
					break;
				}
				t++;
			}
		}
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章