奇怪的電梯(深搜dfs)

Description
有一天uncle-lu做了一個夢,夢見了一種很奇怪的電梯。
大樓的每一層樓都可以停電梯,而且i層樓上有一個數字。電梯只有四個按鈕:開,關,上,下。上下的層數等於當前樓層上的那個數字。
當然,如果不能滿足要求,相應的按鈕就會失靈。例如:3,3,1,2,5代表了,從1樓開始。在1樓,按“上”可以到4樓,按“下”是不起作用的,因爲沒有−2樓。
那麼,從A樓到B樓至少要按幾次按鈕呢?
Input
第一行爲3個用空格隔開的正整數,表示N,A,B。
第二行爲N個用空格隔開的非負整數,表示Ki。
Output
一行,即最少按鍵次數,若無法到達,則輸出−1。
Sample Input 1
5 1 5
3 3 1 2 5
Sample Output 1
3
Hint
1≤N≤200,1≤A,B≤N
Time Limit
1000MS
Memory Limit
256MB

分析:
題目要求從A樓到B樓至少要按幾次電梯,乍一看好像是寬搜,但是再往下看,Output要求若無法到達,輸出-1,說明這題是在考慮可達性的基礎上才考慮最短路徑,即不保證可達性,所以這題本質上是個探索路徑存在性的題,應該是深搜。
在解該題時,用整型變量ans記錄目前搜索到的可行路徑的最小長度,當出現長度(按按鈕次數)大於該最小長度的狀態,不論有沒有到達目標樓層,都不必繼續搜索,以達到剪枝目的。
建議把剪枝語句放在深搜函數最前面,判斷是否到達目標樓層的語句放在其後,以達到最佳效果的剪枝。因爲如果目前狀態是已經到達目標樓層,但是長度(按按鈕次數)不小於之前搜索到的答案,就必定不是答案,可以直接退出深搜函數。如果進入深搜函數體先判斷是否到達目標樓層,則無法實現直接退出函數體,而且還要執行一次選最小值操作(比較當前長度和之前搜索到的長度)。

#include<stdio.h>
#include<algorithm>

int N,A,B;//樓層數、起始樓、目標樓
int step[201];//樓層對應數字
int ans=0xFFFFFFF;//答案,因爲是找最小值,所以置個足夠大的數
bool vis[201]={0},flag=false;//標記已經到過的樓、可達性標記

void dfs(int floor,int cnt)//樓層,按按鈕次數(路徑長度)
{
    if(ans<=cnt){//剪枝
        return;
    }
    if(floor==B){//找到一條可行路徑
        //如果搜索到底都沒進過if(floor==B)
        //則flag仍是false,說明A->B不可達
        flag=true;
        ans=std::min(ans,cnt);
        return;
    }
    //搜索,仍然需要注意:先判斷數組越界再判斷重複訪問
    //N是樓頂,不能再往上了
    if(floor+step[floor]<=N && !vis[floor+step[floor]]){
        vis[floor+step[floor]]=true;//標記
        dfs(floor+step[floor],cnt+1);
        vis[floor+step[floor]]=false;//回溯
    }
    //沒有0樓,不能再往下了
    if(floor-step[floor]>0 && !vis[floor-step[floor]]){
        vis[floor-step[floor]]=true;
        dfs(floor-step[floor],cnt+1);
        vis[floor-step[floor]]=false;
    }
    //枚舉完就退
    return;
}

int main()
{
    scanf("%d%d%d",&N,&A,&B);
    for(int i=1;i<=N;i++)
    {
        scanf("%d",step+i);
    }
    vis[A]=true;//先行給初始樓層標記
    dfs(A,0);//傳入初始狀態
    if(!flag){//不可達
        printf("-1");
    }
    else printf("%d",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章