【練習-7】新飛行棋(BFS)
新飛行棋
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],也就是到達該位置的最少步數。
說的好像有點多,也無所謂了……
以上只是鄙人的拙見,如果有錯誤、不足之處,還請指正。