從迷宮問題 連連看 紅與黑說回溯演算法遍歷解空間

2022-04-07 07:37:41 字數 4203 閱讀 6908

一、迷宮問題:

1792

:迷宮總時間限制: 3000ms 記憶體限制: 65536kb

描述一天extense在森林裡探險的時候不小心走入了乙個迷宮,迷宮可以看成是由n *n的格點組成,每個格點只有2種狀態,.和#,前者表示可以通行後者表示不能通行。同時當extense處在某個格點時,他只能移動到東南西北(或者說上下左右)四個方向之一的相鄰格點上,extense想要從點a走到點b,問在不走出迷宮的情況下能不能辦到。如果起點或者終點有乙個不能通行(為#),則看成無法辦到。

輸入第1行是測試資料的組數k,後面跟著k組輸入。每組測試資料的第1行是乙個正整數n (

1<= n <= 100),表示迷宮的規模是n * n的。接下來是乙個n *n的矩陣,矩陣中的元素為.或者#。再接下來一行是4個整數ha, la, hb, lb,描述a處在第ha行, 第la列,b處在第hb行, 第lb列。注意到ha, la, hb, lb全部是從0開始計數的。

輸出k行,每行輸出對應乙個輸入。能辦到則輸出「yes」,否則輸出「no」。

樣例輸入23

.##..#

#..002

25.....

###.#

..#..

###..

...#.00

40樣例輸出

yesno

對於迷宮問題,如果要求乙個解,那麼回溯法無疑是非常快的,它往往只需要遍歷迷宮中很少的部分——具體遍歷多少要看我們的排序方法和迷宮的情況是否相符。

bool msearch(point curpoint,bool map,int maplen,bool

path)

else

else}}

}return

false

;}

在while迴圈前,首先將第乙個資料壓棧,然後就可以進入迴圈;在迴圈過程中首先獲取當前元素,對當前元素進行處理(此處是判斷是否達到終點)的同時繼續壓棧相關元素(這裡是下一步的可能位置)。這樣直到棧為空時全部元素都被處理一遍,即全部節點都被處理完成。

上面的函式中,使用了乙個與map大小相同的path陣列,它的功能是將當前路徑經過的點記錄下來以防止程式陷入死迴圈和加速,可以看到在處理當前點時,把當前點記錄下來,綠的被我注釋掉的行就是回溯過程把這個點記錄恢復為沒有走過。這裡要思考一下,是否需要恢復走過的路徑上的點:

1、若不恢復,會不會對父節點的兄弟節點或父節點的父節點搜尋產生影響?

2、若恢復,會不會導致搜尋效率降低?

其實這兩個問題很好解答,是否注釋掉要看我們需要多少解。如果只需要乙個解,那麼:

1、若路徑上有解,沒有必要恢復。

2、路徑上沒有解,也沒有必要恢復。

所以,在當前只需要找到一條路徑的這個迷宮問題中,注釋掉可以提高效率。但是,如果需要多個解:

1、若路徑上有解,必須恢復,以備其他路徑再次找到這一段路徑上的某個部分。

2、若路徑上沒有解,那麼沒有必要恢復。

結論是當需要多個解且路徑上有解時,需要恢復路徑,否則不用恢復。

int nextstep(point curpoint,bool map,int maplen,point steps,bool

path)

}//對結果進行排序:距離目標點近的排在後面,是一種貪心思想。

sort(steps,steps+stepcnt,compare);

return

stepcnt;

}

當然,這樣做有一定的風險,因為我們無法證明貪心是有效的,但至少比每次都先像南(下)搜尋靠譜一點——期待迷宮不是被針對設計的吧。就本題來說,測試資料有可能資料不像常人所理解的出入口就在迷宮裡面或者入口和出口是分開的,這是乙個處於空間曲率飄忽不定的空間內的迷宮,一切皆有可能,所以搜尋之前還需要一些界定。把完整**貼在這裡:

#include#include

#include

#include

//回溯法

using

namespace

std;

struct

point;

point outpoint;

point direction[

4]=,,,};//

u,d,l,r

bool compare(const point a,const

point b)

int nextstep(point curpoint,bool map,int maplen,point steps,bool

path)

}//對結果進行排序:距離目標點近的排在後面,是一種貪心思想。

sort(steps,steps+stepcnt,compare);

return

stepcnt;

}bool msearch(point curpoint,bool map,int maplen,bool

path)

else

else}}

}return

false;}

intmain()}}

cin>>curpoint.y>>curpoint.x>>outpoint.y>>outpoint.x;

if(0

<=curpoint.x && curpoint.x0

<=curpoint.y && curpoint.y&& 0

<=outpoint.x && outpoint.x0

<=outpoint.y && outpoint.y&& map[curpoint.x+curpoint.y*maplen] && map[outpoint.x+outpoint.y*maplen])

else

}else

delete

map;

delete

path;

}}

二、連連看

這個問題是這樣的:

怎麼看都是乙個連連看遊戲。這個問題實際上是這樣的:

在乙個寬度高度分別為w,h的面板中,從(1,1)到(w-1,h-1)範圍內放置卡片。即外圍又一圈通路。這樣這個問題就變成迷宮問題,如果只要求乙個路徑,那麼解法相同。但它需要搜尋全部解,並進行比較,找出拐點最少的乙個解。所以,如果限定時間比較短的話,最好還是用bfs。後面會另外寫一篇這個問題的解法。

三、馬走日

這個問題與二相同,題目都要求遍歷全部解。所以,它們都可以用另一種方式來進行搜尋:

void msearch(point curpoint,bool map,int maplen,int depth,bool

path)

if(depth==0 && flag)

else

delete

steps;

}}

這種遞迴的方式也存在回溯過程,但想用它來找到乙個解將比較耗時。

四、紅與黑

這是這樣乙個問題:

總時間限制: 1000ms 記憶體限制: 65536kb

描述有一間長方形的房子,地上鋪了紅色、黑色兩種顏色的正方形瓷磚。你站在其中一塊黑色的瓷磚上,只能向相鄰的黑色瓷磚移動。請寫乙個程式,計算你總共能夠到達多少塊黑色的瓷磚。

輸入包括多個資料集合。每個資料集合的第一行是兩個整數w和h,分別表示x方向和y方向瓷磚的數量。w和h都不超過20。在接下來的h行中,每行包括w個字元。每個字元表示一塊瓷磚的顏色,規則如下

1)『.』:黑色的瓷磚;

2)『#』:白色的瓷磚;

3)『@』:黑色的瓷磚,並且你站在這塊瓷磚上。該字元在每個資料集合中唯一出現一次。

當在一行中讀入的是兩個零時,表示輸入結束。

輸出對每個資料集合,分別輸出一行,顯示你從初始位置出發能到達的瓷磚數(記數時包括初始位置的瓷磚)。

樣例輸入69

....#.

.....#

......

......

......

......

......

#@...#

.#..#. 00

樣例輸出

45

這個問題實際上就是乙個4向的區域填充問題。解決思路很簡單:

1

將種子座標入棧(@的那一點)

2迴圈(結束條件——堆疊是為空)

3 彈出乙個點,若該點為黑色:記錄數+1,設定該點為紅色。將上下左右四向中位於影象內的的點入棧

這就是乙個影象處理的基本**——「快速種子填充」,在我的另乙個部落格上有詳細的vb.net**和優化過程:

連連看小遊戲,遞迴問題

題目源自 程式設計實習 12章 遞迴問題。思路根本方式是遞迴,走迷宮類問題,處在每一步上都要列舉下一步的方向,用move陣列來儲存行進方向。另外用mark陣列來儲存是否訪問過 開始用puzzle陣列來表示板子分布,然後在外面多加一圈來輔助計算。遞迴函式關鍵在於每一次呼叫時,先判斷 當前步數是否大於最...

連連看原始碼除錯問題

2.原先專案的 啟動專案 為mapeditor,因為他下面必要的資源被刪了,所以也不能除錯成功,你可以把啟動專案定為directx,然後編譯完以後,再把那個mapeditor.exe拷過來 如果你需要使用的話 或者你將debug下的資源拷到mapeditor的目錄下去,這樣就可以除錯了 3.原先專案...

連連看一種演算法的實現 分析與思考(下)

問題擴充套件 下面我們來討論這樣乙個問題 如果遊戲規則允許如圖中紫紅色連線的方式,那麼我們改如何實現它呢?我的想法是 1.在p2周圍4 個方向任意乙個空格checkp2,2.進行第三種連線方法中的判斷,triline p1,checkp2 看結果的真假值 如圖所示,途中p1 checkp2進行tri...