布滿障礙物的矩陣中連線兩點所需的最小直線數目

2021-08-03 13:54:41 字數 3103 閱讀 3708

假設有乙個矩陣,四個邊都是堵住的,矩陣內部有若干障礙物,現在有兩個點,用若干條直線連線兩個點,且直接不能穿越障礙物,那麼至少需要幾條直線呢?

前面寫過乙個迷宮中求路徑的程式(  ),用到了遞迴的方式求解。先用類似的方法定義乙個滿足要求的矩陣。對於迷宮的矩陣,四個邊都布滿了障礙物且分別有乙個入口和出口位於其上,在這個問題裡面我們可以簡化一下去掉四邊的圍牆,反正尋找路徑時座標點滿足 0<=x#include #include #include // height & width must be positive ingeters and cannot both be 1

#define height 24

#define width 36

// 2-dimensional array that represents the maze

int matrix[height][width];

int min_nlines = int_max;

// 4 directions:

const int directions[4][2] = , , , };

// status of a cell in the matrix

enum status ;同樣定義了尋找路徑的四個方向,矩陣上每個點的狀態要麼為障礙(-1),要麼為空白格(極大值),在計算之後其值更新為到達起點的步數(也就是連通所需的最小直線數量)。那麼對於起始點,這個值當然為0,對於其他空白和終點,可以先設定乙個非常大的正整數,為了表示區別將終點設定為int_max,其他空白為int_max - 1。接下來則是生成矩陣的程式,沒啥特別之處,先是放置障礙物的函式,其引數定義了空白和障礙物的比例,這個值太小則可能徹底堵死找不到通路,太大則顯得矩陣空空蕩蕩,連通的路徑不夠百曲千回,演示效果不佳,一般取3或4就差不多了。接著是隨機選取起點和終點,兩點不允許重合,為了簡單起見,即使不小心選到障礙物也不用計較,就當它原本是空白格。最後是列印矩陣,可以人為的給四周加上一圈圍牆,每一處空白會顯示為距離起點的步數(當然有些空白是不可達的,假如正好被障礙物圍起來)。這一切看來都沒什麼問題。

void placeobstacle(int ratio)

}}void setupendpoint(int *a, int *b, int *c, int *d)

while (*a == *c && *b == *d);

matrix[*a][*b] = start;

matrix[*c][*d] = end;

}void printmatrix(void)

printf("\n");

for (int row = 0; row < height; ++row)

printf("█\n");

}for (int col = 0; col < width + 2; ++col)

printf("\n");

}

接下來是主函式,其中最關鍵的就是呼叫searchpath這個尋找路徑的函式。

int main(void)

受到前面那個迷宮問題的影響,我沒有進行任何思索就寫了乙個類似的searchpath遞迴函式,形式和之前那個差不多,由於打算把從起點到任意空白處的步數都計算出來,所以沒有設定終止條件。從起點開始,依次選定各個方向,將步數加1後一步步往前探索空白格直到遇見障礙物,若之前計算過的空白格的步數且大於當前數值,那麼保留當前值並在該點進行遞迴呼叫,如下所示:

void searchpath(int start_row, int start_col, int end_row, int end_col, int num_of_lines)

if (next_row == end_row && next_col == end_col && matrix[next_row][next_col] < min_nlines)}}

}

事實上這個方法相當糟糕,計算效率低下。試想一下假如障礙物非常稀少甚至矩陣就是空曠的,連線任意兩點最多隻需要兩條線段,然而我們一步一步前進,不停遞迴,繞來繞去的不斷撞上以前計算過的空白格並更新之。舉個最簡單的例子,本來沿著乙個點朝一條直線看過去,上面的所有空格都該具有同樣的步數n,而採用遞迴的方式,每走一步進入一次遞迴,那麼沿途步數會變成n+1, n+2, n+3, n+4...最後從遞迴裡面退出繼續走內層的迴圈,這些座標的步數又被還原成n,那麼為何不一開始就將四個方向上能直達的點的步數設定為n呢?再仔細想想,這不就是乙個廣度優先的搜尋嗎,而前面的遞迴方法是深度優先的,放到這個題目中顯然不適用。於是重新實現了這個searchpath函式,廣度搜尋通常需要乙個輔助佇列,為了方便這裡不用鍊錶而是直接開乙個大的二維陣列,每行可以儲存單個空白格的兩個座標值。從起點開始擴散,一旦位於四條射線上的空白格的步數比原來的小,那麼該座標被加入佇列準備進行下一次嘗試,**如下:

void searchpath(int start_row, int start_col, int end_row, int end_col)

}; int head = 0, tail = 1;

while (head != tail)

if (next_row == end_row && next_col == end_col && matrix[next_row][next_col] < min_nlines)}}

head = (head + 1) % (height * width);}}

採用這個新函式再試了下,程式執行速度比之前不知快了多少倍。由此要吸取教訓,遇到問題時先思考,而不是冒然套用以往的經驗,經驗固然能幫助解決一些問題,但是慎重的思考還是要放在第一位。在這個程式的執行如下所示:

平時愛玩的連連看,滿足消除的條件無非就是兩個方塊間的連線數最多為3,每消去一對方塊就要做一次檢測看看是否還能繼續遊戲。哪怕初始生成的牌面是有辦法完全消除的,但玩家執行的步驟還是可能導致陷入死局,所以遊戲裡面常有「全部重排」的道具。即時檢測應該不會太耗時間,畢竟只要有一對牌可消去便可終止測試,況且只需要完成步數為3的廣度搜尋就行了。即便是牌的數量非常密集需要蒐個遍才能終止,這個過程以計算機的視角來看也不會太耗時(在這種情況下單張牌的搜尋其實耗時少,四周都密不透風嘛)。實際開發中或許有更快的辦法。

js 畫兩點之間的連線

案例根據兩點,計算出兩點之間的夾角,然後利用css3 旋轉的角度,畫出斜線 計算兩點之間位移 根據長的一條邊,乙個畫素乙個畫素的拆分為點,然後乙個乙個拼湊 0 獲得人物中心和滑鼠座標連線,與y軸正半軸之間的夾角 function getangle x1,y1,x2,y2 if x2 x1 y2 y1...

計算樹中兩點之間的距離

題目 要求倒不麻煩,乙個節點資料不重複的二叉樹,設其元素型別為整型,找出最小元素與最大元素之間的路徑長度,即兩個節點之間的連線距離,不是節點個數。過程 以陣列元素來構建二叉樹,自定義陣列為,以 2 i 1,2 i 2 表示子節點,構建樹結構如圖 public class t 二叉樹基本節點資料 tr...

前端工作中的兩點優化

為了解決第乙個問題,我先將接收到的兩個點如 x1,y1 x2,y2 再將兩個點構造成四個點 x1,y1 x2,y2 x1,y2 x2,y1 這四個點,然後再將四個點帶入之前寫好的 中,這次基本能實現功能。但是仍然沒有解決第二個問題,也是對業務最重要的問題。當時同事建議我將矩形框對資料的約束新增到sq...