天天看點

POJ2488-A Knight's Journey(DFS水題小白詳解)

水題還要詳解???

有此疑問的請自行屏蔽此部落格,水題也是針對解題對象來區分的,對于剛剛接觸搜尋的大一新生,我認為,有些地方還是值得斟酌注意的,希望不喜勿噴。

雖然我已經大二了,但是代碼能力并不強,希望能堅持刷題,堅持寫部落格,認認真真寫解題報告,一方面是對自我的總結,另一方面是希望能在部落格上留下些什麼,簡單地說思路,貼貼代碼,對讀者也是不負責任的表現。

附上學長留下的搜尋題集,我會堅持做題,堅持發解題報告的。

專題四(簡單搜尋)

★★★★★?關于dfs,新接觸的話建議手動畫一畫遞歸樹,一層一層地去模拟,會幫助你了解,比看着代碼相面要實用的多!!!

題目大意:給一個棋盤,問能不能以馬的跳法不重複、無遺漏地将棋盤周遊一遍。如果可以的話,按照字典序輸出排列在先的一條路徑。

很裸的一道題目,但是有幾個需要注意的地方。

1、 馬的走法,如題目中給出的圖檔所示,和中國象棋中“馬走日”的方式類似,走一個2*1矩形的對角線。

POJ2488-A Knight's Journey(DFS水題小白詳解)

2、“按照字典序輸出排列在先”是什麼意思呢?

兩種情況:

①針對字母:A1B2C3D4排列在A1C3B2D4之前

②針對數字:A1B2C3D4排列在A2B2C3D4之前

是以,在進行深搜的時候,八個位置的先後順序應該如下圖紅色數字标注那樣。否則會WA。

POJ2488-A Knight's Journey(DFS水題小白詳解)

于是,我們給出移動的坐标變換數組,分别對應上述八個位置的移動:

一定要按照順序!否則必WA。

int dx[8]={-1,1,-2,2,-2,2,-1,1};///x為行數,縱坐标
int dy[8]={-2,-2,-1,-1,1,1,2,2};///y為列數,橫坐标
           

3、 dfs的四小步:目前操作、結束條件、搜尋條件、是否有回溯操作

這四小步也是我掌握dfs的關鍵。

其中,本題需要注意“回溯”的部分。

for(int i=0;i<8;i++){
        int nx=x+dx[i];
        int ny=y+dy[i];
        ///搜尋條件
        if(nx>=1&&nx<=p&&ny>=1&&ny<=q&&!book[nx][ny]&&!success){
            book[nx][ny]=true;
            dfs(nx,ny,num+1);
            ///極為重要,回溯的思想,要去想搜尋失敗的時候,目前位置的通路标記要變回false
            book[nx][ny]=false;
        }
    }
           

如上述代碼:

①按照0-7的順序進行移動,判斷是否達到搜尋條件(請注意,此處除了需要滿足不跳出棋盤外,還需要滿足下一個點未被通路和還沒有成功周遊整個棋盤兩個條件)。

②如果是,進入if語句,标記(nx,ny)已被通路,繼續dfs,将(nx,ny)的通路标記删除。

★★★★★?不斷dfs過程中,發現某一位置的下一步的八個位置要麼在來的路上被通路過了,要麼出界了,且此時整個棋盤還未周遊完,這說明,這條路徑是不通的,那麼在結束本次遞歸的時候,需要将原來标記的通路删除掉,以免在嘗試另外一種走法時産生錯誤,因為這個位置的通路與否直接影響了下一條路徑搜尋的結果。

4、 還有一個需要注意的小地方,小技巧,就是序号與字母的對應轉換。

輸出路徑代碼:

for(int j=0;j<p*q;j++){
       printf("%c%d",path[j].y+'A'-1,path[j].x);
}
           

因為代碼中x代表行數1-26,y代表列數A-Z。而輸出要列在先,行在後,是以先y後x。

數字1+‘A’-1=‘A’,2+‘A’-1=B,以此類推。

同理,字母轉化成序号就是 -‘A’ 或者 -‘a’ 。

參考代碼:

#include<stdio.h>
#define maxn 27
struct path{
    int x;
    int y;
}path[maxn];
int num,p,q;
bool book[maxn][maxn];
bool success=false;
///注意字典序
int dx[8]={-1,1,-2,2,-2,2,-1,1};///x為行數,縱坐标
int dy[8]={-2,-2,-1,-1,1,1,2,2};///y為列數,橫坐标
void dfs(int x,int y,int num)
{
    ///目前節點加入路徑
    path[num].x=x;
    path[num].y=y;
    ///結束條件
    if(num==p*q-1){
        success=true;
        return;
    }
    for(int i=0;i<8;i++){
        int nx=x+dx[i];
        int ny=y+dy[i];
        ///搜尋條件
        if(nx>=1&&nx<=p&&ny>=1&&ny<=q&&!book[nx][ny]&&!success){
            book[nx][ny]=true;
            dfs(nx,ny,num+1);
            ///極為重要,回溯的思想,要去想搜尋失敗的時候,目前位置的通路标記要變回false
            book[nx][ny]=false;
        }
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        num=0;
        success=false;
        scanf("%d%d",&p,&q);
        for(int t=1;t<=p;t++)
            for(int k=1;k<=q;k++)
                book[t][k]=false;
        ///以上為初始化
        book[1][1]=true;
        dfs(1,1,0);
        printf("Scenario #%d:\n",i);
        if(!success){
            printf("impossible\n");
        }
        else{
            for(int j=0;j<p*q;j++){
                printf("%c%d",path[j].y+'A'-1,path[j].x);
            }
            printf("\n");
        }
        printf("\n");
    }
    return 0;
}