1. 程式人生 > 其它 >【練習-7】新飛行棋(BFS)

【練習-7】新飛行棋(BFS)

技術標籤:練習冊1c++c語言

新飛行棋

Description
期末考試終於結束了。Andy同學感覺鬆了一口氣,他決定重溫小時候的快樂時光–下飛行棋。但是他弄丟了傳統飛行棋需要的骰子,因此他發明了一種新型的飛行棋遊戲,規則如下:棋盤上有n個格子,由近到遠分別編號為1到n。對於1≤i≤n,第i個格子上寫著一個正整數Ni。當玩家處於第a個格子時,他可以選擇往後走Na步,或者往前倒退Na步。當然如果Na+a>n,那麼他就只能選擇後退;同理如果a−Na<1,那麼他就只能選擇前進。保證不會出現既不能前進又不能後退的格子。

Andy學完程式設計後對一個問題很感興趣:從編號s出發,至少需要經過幾把,可以到達t點?(例如在a點選擇往前走Na步,稱之為一把)。

Input
第一行三個整數,分別為n,s,t意義如題面所述。
第二行n個正整數,第i個數為Ni。

Output
一個數,為最少經過的把數。如果s無法到達t,輸出-1。

Samples
Input
6 6 4
1 2 2 3 1 2
Output
1
Hint
對於前10%的資料,s=t;
對於前40%的資料,n≤200;
對於另外10%的資料,s無法到達t;
對於100%的資料,n≤200000;
Source
石光中學 FCS2018基礎班day 6

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =
200005; int n,s,t; int a[N],cnt[N],k[N],u,v; void bfs() { queue<int> q; q.push(s);k[s]=1;cnt[s]=0; while(q.size()) { int x = q.front(); q.pop(); u = x+a[x]; v = x-a[x]; if(u<=n&&!k[u]) { k[u]=1; cnt[u] = cnt[x]+1; q.push(u); } if(v>=1&&!k[v]) { k[
v]=1; cnt[v] = cnt[x]+1; q.push(v); } } } int main() { cin>>n>>s>>t; for(int i=1;i<=n;i++) { cin>>a[i]; cnt[i]=-1; } bfs(); cout<<cnt[t]; return 0; }

這個是宇巨教的……
大題思路:
一個數組cnt[]來記錄到達該位置時,已經走的步數。這個陣列最初的時候初始化為-1,因為如果沒有答案的話(無法到達)可以直接輸出-1,(不能判斷cnt是否為0,因為0是有意義的,當起始位置就是終點的時候cnt可以為0)

一個數組k[]來標記是否走過這個位置(因為同一個位置不可以走兩次)

然後我們看函式主體:
這裡是用到了FIFO佇列queue<int> q; q.push(s);k[s]=1;cnt[s]=0;我們把起始位置s壓入佇列,然後標記k[s]=1,表示這裡已經走過了不能再來了,並且cnt[s]=0,表示這裡是起始位置。

	while(q.size())
	{
		int x = q.front();
		q.pop();
		u = x+a[x];
		v = x-a[x];
		if(u<=n&&!k[u])
		{
			k[u]=1;
			cnt[u] = cnt[x]+1;
			q.push(u);
		}
		if(v>=1&&!k[v])
		{
			k[v]=1;
			cnt[v] = cnt[x]+1;
			q.push(v);
		}
	}

很顯然這樣的話只要佇列裡有數就會不斷地測試,就會測試到每個可以到達的點
①這裡的u很顯然就是向前走的操作(x+a[x]),而v就是向後走的操作(x-a[x])。
②如果向前走在[1,n]範圍內,並且之前沒有到達過,就把這個點壓入佇列,並且步數+1。同樣,如果向後走在[1,n]範圍內,並且之前沒有到達過,就把這個點壓入佇列,並且步數+1。
那麼為什麼要判定是否走過這個點?
因為如果先前已經走過了那麼接下來的操作一定是重合的,並且先到達的在時間上一定是優先的。
在這裡插入圖片描述
可以參考上面的圖:
上面的過程需要3步才能到達4,而下面的過程只需要2步就能到達4,並且到達4以後的路徑不會因為選擇了上面或是下面的過程而改變。因此,先到達的在時間上一定是優先的。

我們這樣進行操作,最終一定是每次測試同一時間點到達的數
例如:我們
例如:我們最初的起始點是1,如果之後的操作我們可以向前也可以向後,那麼就會壓入兩個數值,實際上這兩個數值的先後順序並不重要,因為他們都是屬於第2層(cnt[] = 1)我們把第二層的點都測試完了之後,才會去測試第3層的數(cnt[] = 2)。
模擬一下佇列如下圖:
在這裡插入圖片描述
最初只有第1層,第1層操作時會產生2個第2層的數,之後第2層的數操作會產生第3層的數……

總之,在完成了以上的操作時,我們已經按照最優的時間到達了每一個能到達的點,並在那裡留下了我們的標記。
最後直接輸出cnt[t],也就是到達該位置的最少步數。

說的好像有點多,也無所謂了……

以上只是鄙人的拙見,如果有錯誤、不足之處,還請指正。