第1部分 基礎算法(提高篇)--第4章 廣搜的優化技巧-1450:【例 3】Knight Moves

1450:【例 3】Knight Moves

時間限制: 1000 ms 內存限制: 65536 KB
提交數: 1170 通過數: 406
【題目描述】
原題來自:POJ 1915

編寫一個程序,計算一個騎士從棋盤上的一個格子到另一個格子所需的最小步數。騎士一步可以移動到的位置由下圖給出。
在這裏插入圖片描述
【輸入】
第一行給出騎士的數量 n。

在接下來的 3n 行中,每 3 行描述了一個騎士。其中,

第一行一個整數 L 表示棋盤的大小,整個棋盤大小爲 L×L;

第二行和第三行分別包含一對整數 (x,y),表示騎士的起始點和終點。假設對於每一個騎士,起始點和終點均合理。

【輸出】
對每一個騎士,輸出一行一個整數表示需要移動的最小步數。如果起始點和終點相同,則輸出 0。

【輸入樣例】
3
8
0 0
7 0
100
0 0
30 50
10
1 1
1 1
【輸出樣例】
5
28
0
【提示】
對於 100% 的數據,有 4≤L≤300,保證 0≤x,y≤L−1。


思路:
這道題可以用雙向寬度搜索優化給定了起始狀態和結束狀態,求最少步數,顯然是用BFS,爲了節省時間,選擇雙向BFS。雙向BFS,即從起點向終點搜,從終點向起點搜,擴展各自的狀態,直到出現兩者擴展的狀態重合優化:每次選擇結點少的擴展。
在這裏插入圖片描述

```#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>

using namespace std;

struct node
{
    int x,y;
}q[2][100001];     //定義兩個隊列 
int text,ans,n,l[2],r[2];
int dis[2][301][301],v[2][301][301];
int dx[8]={-2,-2,-1,-1,1,1,2,2};    //位移 
int dy[8]={-1,1,-2,2,-2,2,-1,1};

int expand(int k)    //對隊列 k 進行擴展 
{
    int t,i,j,x,y,d,tx,ty;
    x=q[k][l[k]].x;  
    y=q[k][l[k]].y;
    d=dis[k][x][y];
    for(int i=0;i<8;i++)  //八個方向擴展 
    {
        tx=x+dx[i];    //新點 
        ty=y+dy[i];
        
        if(tx>=0&&tx<=n&&ty>=0&&ty<=n&&!v[k][tx][ty])   //合法而且沒走過 
        { 
            v[k][tx][ty]=1;  //標記走過 
            r[k]++;          //入隊 
            q[k][r[k]].x=tx;
            q[k][r[k]].y=ty;
            dis[k][tx][ty]=d+1; //記錄步數 
            if(v[1-k][tx][ty])  
            //判斷另一個隊列中是否已經走過這個點,也就是判斷是否重合相遇 
            //如果相遇,就找到了一條完整的最短路徑
            //k=0時,1-k=1
            //k=1時,1-k=0 
            {
                ans=dis[k][tx][ty]+dis[1-k][tx][ty];
                return 1;
            }
        }
    }
    return 0;
}

void bfs()
{
    if(q[0][1].x==q[1][1].x&&q[0][1].y==q[1][1].y) //起點終點本就相同 
    {
        ans=0;  return;
    }
    v[0][q[0][1].x][q[0][1].y]=1;  //標記走過 
    v[1][q[1][1].x][q[1][1].y]=1;
    l[0]=r[0]=1;    //初始化頭指針尾指針 
    l[1]=r[1]=1;
    while(l[0]<=r[0]&&l[1]<=r[1])  //兩個隊列都非空,先擴展結點數少的 
    {
        if(r[0]-l[0]<r[1]-l[1])
        {
           if(expand(0))  return;  //找到答案啦 
           l[0]++;   // 沒找到,移動頭指針繼續找 
        }
        if(r[0]-l[0]>=r[1]-l[1])
        {
            if(expand(1))  return;
            l[1]++;
        }
    }
    
}

int main()
{
    scanf("%d",&text);
    for(int i=1;i<=text;i++)  //多組數據 
    {
        memset(dis,0,sizeof(dis));  
        memset(v,0,sizeof(v));
        memset(q,0,sizeof(q));
        
        scanf("%d",&n);  n=n-1;  
        scanf("%d%d",&q[0][1].x,&q[0][1].y);   //起點 
        scanf("%d%d",&q[1][1].x,&q[1][1].y);   //終點 
        bfs();
        printf("%d\n",ans);
    }
    
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章