1. 程式人生 > >BFS簡單專題總結 BFS的入門之路

BFS簡單專題總結 BFS的入門之路

先給出BFS的思路吧,大概都是這樣做的

struct node
{
	int x, y, step;
};//根據需要定義結構體
int vis[MAX][MAX]={0};//二維三維之類的看題目給的條件
queue<node> q;
q.push(start);//把起點壓入佇列
node pre,nex;
vis[start.x][start.y]=1;//一般是設定為1,如果有其他需要可以更改
while(!q.empty()){
    pre = q.front();
    if(xxx) break; //xxx是終點的判斷
    q.pop();
    for(int i=0;i<4;i++){//i不一定是小於4,看題目來定
        if(xxxx){//如果滿足怎樣的條件將這種情況壓入佇列
            q.push(xx);
            vis[xx.x][xx.y]=1;//別忘了標記來過
        }
    }
}
if(!q.empty()){
    //就是找到了終點
}
else{
    //沒有找到終點
}

入門的是遇到的一道馬跳題,比較簡單

Knight Moves

A friend of you is doing research on the Traveling Knight Problem (TKP) where you are to find the shortest closed tour of knight moves that visits each square of a given set of n squares on a chessboard exactly once. He thinks that the most difficult part of the problem is determining the smallest number of knight moves between two given squares and that, once you have accomplished this, finding the tour would be easy.
Of course you know that it is vice versa. So you offer him to write a program that solves the "difficult" part.

Your job is to write a program that takes two squares a and b as input and then determines the number of knight moves on a shortest route from a to b.

Input Specification

The input file will contain one or more test cases. Each test case consists of one line containing two squares separated by one space. A square is a string consisting of a letter (a-h) representing the column and a digit (1-8) representing the row on the chessboard.

Output Specification

For each test case, print one line saying "To get from xx to yy takes n knight moves.".

Sample Input

e2 e4a1 b2b2 c3a1 h8a1 h7h8 a1b1 c3f6 f6

Sample Output

To get from e2 to e4 takes 2 knight moves.To get from a1 to b2 takes 4 knight moves.To get from b2 to c3 takes 2 knight moves.To get from a1 to h8 takes 6 knight moves.To get from a1 to h7 takes 5 knight moves.To get from h8 to a1 takes 6 knight moves.To get from b1 to c3 takes 1 knight moves.To get from f6 to f6 takes 0 knight moves.

題意:馬步踏棋,日字走法,問你幾步能到達終點。所以方向有八個!!!

#include <stdio.h>
#include <queue>
using namespace std;
struct node
{char x;
int y,step;};
int main(){
    char x1,x2;
    int y1,y2;
    queue<node> q;
    node start,temp;
    while(~scanf("%c%d %c%d",&x1,&y1,&x2,&y2)){
            getchar();
        int vis[150][9]={0};
        for(int i=0;i<150;i++)
            for(int j=0;j<9;j++)
                vis[i][j]=0;
        while(!q.empty())//清空
                q.pop();
        start.x=x1;start.y=y1;start.step=0;
        q.push(start);
        vis[start.x][start.y]=1;
        while(!q.empty()){
            start = q.front();
            if(start.x==x2&&start.y==y2)break;
            q.pop();
            //2,1
            temp.x=start.x+2;temp.y=start.y+1;
            if(temp.x<='h'&&temp.y<=8&&vis[temp.x][temp.y]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.x][temp.y]=1;
            }
            //2,-1
            temp.x=start.x+2;temp.y=start.y-1;
            if(temp.x<='h'&&temp.y>0&&vis[temp.x][temp.y]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.x][temp.y]=1;
            }
            //-2,1
            temp.x=start.x-2;temp.y=start.y+1;
            if(temp.x>='a'&&temp.y<=8&&vis[temp.x][temp.y]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.x][temp.y]=1;
            }
            //-2,-1
            temp.x=start.x-2;temp.y=start.y-1;
            if(temp.x>='a'&&temp.y>=1&&vis[temp.x][temp.y]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.x][temp.y]=1;
            }
            //1,2
            temp.x=start.x+1;temp.y=start.y+2;
            if(temp.x<='h'&&temp.y<=8&&vis[temp.x][temp.y]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.x][temp.y]=1;
            }
            //1,-2
            temp.x=start.x+1;temp.y=start.y-2;
            if(temp.x<='h'&&temp.y>0&&vis[temp.x][temp.y]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.x][temp.y]=1;
            }
            //-1,2
            temp.x=start.x-1;temp.y=start.y+2;
            if(temp.x>='a'&&temp.y<=8&&vis[temp.x][temp.y]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.x][temp.y]=1;
            }
            //-1,-2
            temp.x=start.x-1;temp.y=start.y-2;
            if(temp.x>='a'&&temp.y>=1&&vis[temp.x][temp.y]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.x][temp.y]=1;
            }
        }
        printf("To get from %c%d to %c%d takes %d knight moves.\n",x1,y1,x2,y2,q.front().step);
    }
}

當時沒有用一個數組把8種情況存下來,所以造成程式碼量比較多。。。

然後就是另一種BFS了(基礎型別的變形)

A strange lift

Problem DescriptionThere is a strange lift.The lift can stop can at every floor as you want, and there is a number Ki(0 <= Ki <= N) on every floor.The lift have just two buttons: up and down.When you at floor i,if you press the button "UP" , you will go up Ki floor,i.e,you will go to the i+Ki th floor,as the same, if you press the button "DOWN" , you will go down Ki floor,i.e,you will go to the i-Ki th floor. Of course, the lift can't go up high than N,and can't go down lower than 1. For example, there is a buliding with 5 floors, and k1 = 3, k2 = 3,k3 = 1,k4 = 2, k5 = 5.Begining from the 1 st floor,you can press the button "UP", and you'll go up to the 4 th floor,and if you press the button "DOWN", the lift can't do it, because it can't go down to the -2 th floor,as you know ,the -2 th floor isn't exist.
Here comes the problem: when you are on floor A,and you want to go to floor B,how many times at least he has to press the button "UP" or "DOWN"?

InputThe input consists of several test cases.,Each test case contains two lines.
The first line contains three integers N ,A,B( 1 <= N,A,B <= 200) which describe above,The second line consist N integers k1,k2,....kn.
A single 0 indicate the end of the input.
OutputFor each case of the input output a interger, the least times you have to press the button when you on floor A,and you want to go to floor B.If you can't reach floor B,printf "-1".
Sample Input5 1 53 3 1 2 50
Sample Output3

題意:有一個特別的電梯,第i層有一個對應的數字ki, 對於第i層按上升鍵up可升上到i+k[i]層,按下降鍵down到達i-k[i]層,到達的樓層最高不能超過n層,最低不能小於1層。給你一個起點A和終點B,問最少要按幾次上升鍵或者下降鍵到達目的地。

思路:多用一個數組記錄每一層樓能咋走,其實就是用這個把For迴圈的那部分代替了,就是這部分創新,其實自己思考下也能做出來的

#include<bits/stdc++.h>
using namespace std;
struct node
{int lou,step;};
int main(){
    int a,b,c;
    queue<node> q;
    node start,temp;
    while(scanf("%d",&a)&&a!=0){
        scanf("%d %d",&b,&c);
        int floor[200]={0},vis[200]={0};
        for(int i=1;i <= a;i++){
            scanf("%d",&floor[i]);
            vis[i]=0;
        }
        while(!q.empty())//清空
                q.pop();
        start.lou=b;start.step=0;
        q.push(start);
        vis[start.lou]=1;
        while(!q.empty()){
            start = q.front();
            if(start.lou==c)break;
            q.pop();
            temp.lou=start.lou+floor[start.lou];
            if(temp.lou<=a&&vis[temp.lou]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.lou]=1;
            }
            temp.lou=start.lou-floor[start.lou];
            if(temp.lou>0&&vis[temp.lou]!=1){
                temp.step=start.step+1;
                q.push(temp);
                vis[temp.lou]=1;
            }
        }
        if(q.empty()) printf("-1\n");
        else printf("%d\n",q.front().step);
    }
}

又是一種新型別!!!劃重點!!

逃離迷宮

Problem Description  給定一個m × n (m行, n列)的迷宮,迷宮中有兩個位置,gloria想從迷宮的一個位置走到另外一個位置,當然迷宮中有些地方是空地,gloria可以穿越,有些地方是障礙,她必須繞行,從迷宮的一個位置,只能走到與它相鄰的4個位置中,當然在行走過程中,gloria不能走到迷宮外面去。令人頭痛的是,gloria是個沒什麼方向感的人,因此,她在行走過程中,不能轉太多彎了,否則她會暈倒的。我們假定給定的兩個位置都是空地,初始時,gloria所面向的方向未定,她可以選擇4個方向的任何一個出發,而不算成一次轉彎。gloria能從一個位置走到另外一個位置嗎?Input  第1行為一個整數t (1 ≤ t ≤ 100),表示測試資料的個數,接下來為t組測試資料,每組測試資料中,
  第1行為兩個整數m, n (1 ≤ m, n ≤ 100),分別表示迷宮的行數和列數,接下來m行,每行包括n個字元,其中字元'.'表示該位置為空地,字元'*'表示該位置為障礙,輸入資料中只有這兩種字元,每組測試資料的最後一行為5個整數k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m),其中k表示gloria最多能轉的彎數,(x1, y1), (x2, y2)表示兩個位置,其中x1,x2對應列,y1, y2對應行。
Output  每組測試資料對應為一行,若gloria能從一個位置走到另外一個位置,輸出“yes”,否則輸出“no”。Sample Input25 5...***.**...........*....1 1 1 1 35 5...***.**...........*....2 1 1 1 3Sample Outputno yes

題解:這是一種新的型別,如果沒有用優先佇列的話要給vis賦予其新的意義,我是讓

他記錄走過的步數,等貼出程式碼之後再進行解釋為什麼要這麼做

#include<bits/stdc++.h>
using namespace std;
struct node
{int x,y,wan,fangxiang;};
int vis[101][101];
int main(){
    int x1,x2,y1,y2,k;
    int a,b,c;
    queue<node> q;
    node start,temp;
    scanf("%d",&a);
    while(a--){
        char map[101][101]={'\0'};
        scanf("%d %d",&b,&c);
        for(int i=0;i<b;i++)
            scanf("%s",map[i]);
        scanf("%d %d %d %d %d",&k,&y1,&x1,&y2,&x2);
        if(x1==x2&&y1==y2){
            printf("yes\n");
            continue;
        }
        while (!q.empty())
            q.pop();
        memset(vis,0,sizeof(vis));
        start.x=x1-1;
        start.y=y1-1;
        start.wan=0;
        start.fangxiang=0;
        vis[start.x][start.y]=1;
        q.push(start);
        while(!q.empty()){
            start=q.front();
            if(start.x==x2-1&&start.y==y2-1&&start.wan<=k+1)break;
            q.pop();
            //向上
            temp.x=start.x+1;temp.y=start.y;
            if(start.fangxiang==1)temp.wan=start.wan;
            else temp.wan=start.wan+1;
            if((vis[temp.x][temp.y]==0||vis[temp.x][temp.y]>=temp.wan)&&temp.x<b&&map[temp.x][temp.y]=='.'&&start.wan<=k+1){
                //printf("up temp.x:%d     temp.y:%d\n",temp.x,temp.y);
                if(start.fangxiang==1){
                    temp.fangxiang=1;
                    temp.wan=start.wan;
                }
                else{
                    temp.fangxiang=1;
                    temp.wan=start.wan+1;
                }
                vis[temp.x][temp.y]=temp.wan;
                //printf("turn:%d\n",temp.wan);
                q.push(temp);
            }

            //向右
            temp.x=start.x;temp.y=start.y+1;
            if(start.fangxiang==2)temp.wan=start.wan;
            else temp.wan=start.wan+1;
            if((vis[temp.x][temp.y]==0||vis[temp.x][temp.y]>=temp.wan)&&temp.y<c&&map[temp.x][temp.y]=='.'&&start.wan<=k+1){
                //printf("right temp.x:%d     temp.y:%d\n",temp.x,temp.y);
                if(start.fangxiang==2){
                    temp.fangxiang=2;
                    temp.wan=start.wan;
                }
                else{
                    temp.fangxiang=2;
                    temp.wan=start.wan+1;
                }
                vis[temp.x][temp.y]=temp.wan;
                //printf("turn:%d\n",temp.wan);
                q.push(temp);
            }

            //向下
            temp.x=start.x-1;temp.y=start.y;
            if(start.fangxiang==3)temp.wan=start.wan;
            else temp.wan=start.wan+1;
            if(temp.x>=0&&(vis[temp.x][temp.y]==0||vis[temp.x][temp.y]>=temp.wan)&&map[temp.x][temp.y]=='.'&&start.wan<=k+1){
                //printf("down\n");
                if(start.fangxiang==3){
                    temp.fangxiang=3;
                    temp.wan=start.wan;
                }
                else{
                    temp.fangxiang=3;
                    temp.wan=start.wan+1;
                }
                vis[temp.x][temp.y]=temp.wan;
                //printf("turn:%d\n",temp.wan);
                q.push(temp);
            }

            //向左
            temp.x=start.x;temp.y=start.y-1;
            if(start.fangxiang==4)temp.wan=start.wan;
            else temp.wan=start.wan+1;
            if(temp.y>=0&&(vis[temp.x][temp.y]==0||vis[temp.x][temp.y]>=temp.wan)&&map[temp.x][temp.y]=='.'&&start.wan<=k+1){
                //printf("left\n");
                if(start.fangxiang==4){
                    temp.fangxiang=4;
                    temp.wan=start.wan;
                }
                else{
                    temp.fangxiang=4;
                    temp.wan=start.wan+1;
                }
                vis[temp.x][temp.y]=temp.wan;
                q.push(temp);
            }
        }
        if(q.empty()){
            printf("no\n");
        }
        else
            printf("yes\n");
    }
}

現在來解釋為啥需要這麼做,這其實因為先左再下與先下再左步數雖然一致,但是從(0,0)到(1,1)之後,(1,1)的方向可不一樣,下一次向下或者向左很影響轉彎數的增減,所以要標記

又是一種新型別的變形!!!劃重點!!

Ignatius and the Princess I

Problem DescriptionThe Princess has been abducted by the BEelzebub feng5166, our hero Ignatius has to rescue our pretty Princess. Now he gets into feng5166's castle. The castle is a large labyrinth. To make the problem simply, we assume the labyrinth is a N*M two-dimensional array which left-top corner is (0,0) and right-bottom corner is (N-1,M-1). Ignatius enters at (0,0), and the door to feng5166's room is at (N-1,M-1), that is our target. There are some monsters in the castle, if Ignatius meet them, he has to kill them. Here is some rules:

1.Ignatius can only move in four directions(up, down, left, right), one step per second. A step is defined as follow: if current position is (x,y), after a step, Ignatius can only stand on (x-1,y), (x+1,y), (x,y-1) or (x,y+1).
2.The array is marked with some characters and numbers. We define them like this:
. : The place where Ignatius can walk on.
X : The place is a trap, Ignatius should not walk on it.
n : Here is a monster with n HP(1<=n<=9), if Ignatius walk on it, it takes him n seconds to kill the monster.

Your task is to give out the path which costs minimum seconds for Ignatius to reach target position. You may assume that the start position and the target position will never be a trap, and there will never be a monster at the start position.

InputThe input contains several test cases. Each test case starts with a line contains two numbers N and M(2<=N<=100,2<=M<=100) which indicate the size of the labyrinth. Then a N*M two-dimensional array follows, which describe the whole labyrinth. The input is terminated by the end of file. More details in the Sample Input.

OutputFor each test case, you should output "God please help our poor hero." if Ignatius can't reach the target position, or you should output "It takes n seconds to reach the target position, let me show you the way."(n is the minimum seconds), and tell our hero the whole path. Output a line contains "FINISH" after each test case. If there are more than one path, any one is OK in this problem. More details in the Sample Output.

Sample Input5 6.XX.1...X.2.2...X....XX.XXXXX.5 6.XX.1...X.2.2...X....XX.XXXXX15 6.XX.....XX1.2...X....XX.XXXXX.
Sample OutputIt takes 13 seconds to reach the target position, let me show you the way.1s:(0,0)->(1,0)2s:(1,0)->(1,1)3s:(1,1)->(2,1)4s:(2,1)->(2,2)5s:(2,2)->(2,3)6s:(2,3)->(1,3)7s:(1,3)->(1,4)8s:FIGHT AT (1,4)9s:FIGHT AT (1,4)10s:(1,4)->(1,5)11s:(1,5)->(2,5)12s:(2,5)->(3,5)13s:(3,5)->(4,5)FINISHIt takes 14 seconds to reach the target position, let me show you the way.1s:(0,0)->(1,0)2s:(1,0)->(1,1)3s:(1,1)->(2,1)4s:(2,1)->(2,2)5s:(2,2)->(2,3)6s:(2,3)->(1,3)7s:(1,3)->(1,4)8s:FIGHT AT (1,4)9s:FIGHT AT (1,4)10s:(1,4)->(1,5)11s:(1,5)->(2,5)12s:(2,5)->(3,5)13s:(3,5)->(4,5)14s:FIGHT AT (4,5)FINISHGod please help our poor hero.FINISH

題解:給你一個圖,圖上的標誌如題所述,碰到怪獸就一定要把它殺死,求花時間最少的最短路徑。

這個題賊好,真的賊好,有很多新穎的點,這個也要賦予VIS陣列實際意義,詳細的貼完程式碼說

#include<bits/stdc++.h>
using namespace std;
struct node
{int x,y,time;};
int vis[120][120];
int a,b;
int turn[4][2]={0,1,1,0,0,-1,-1,0};
node start,temp;
bool check(int x,int y,int z){
    if(x>=0&&y>=0&&x<a&&y<b&&(vis[x][y]==0||vis[x][y]>z))
        return true;
    return false;
}
int main(){
    node roate[120][120];
    int i;
    while(~scanf("%d %d",&a,&b)){
        memset(roate,0,sizeof(roate));
        char Map[120][120]={'\0'};
        memset(Map,0,sizeof(Map));
        memset(vis,0,sizeof(vis));
        for(i=0;i<a;i++){
            scanf("%s",&Map[i]);
        }
        queue<node> q;
        while(!q.empty())
            q.pop();
        start.x=0;
        start.y=0;
        start.time=0;
        q.push(start);
        while(!q.empty()){
            start=q.front();
            q.pop();
            for(i=0;i<4;i++){
                temp.x=turn[i][0]+start.x;
                temp.y=turn[i][1]+start.y;
                if(Map[temp.x][temp.y]=='.')
                    temp.time=start.time+1;
                if(Map[temp.x][temp.y]<='9'&&Map[temp.x][temp.y]>='1')
                    temp.time=start.time+Map[temp.x][temp.y]-'0'+1;
                if(Map[temp.x][temp.y]=='.'&&check(temp.x,temp.y,temp.time)){
                    vis[temp.x][temp.y]=temp.time;
                    q.push(temp);
                    roate[temp.x][temp.y]=start;
                }
                if(Map[temp.x][temp.y]<='9'&&Map[temp.x][temp.y]>='1'&&check(temp.x,temp.y,temp.time)){
                    vis[temp.x][temp.y]=temp.time;
                    q.push(temp);
                    roate[temp.x][temp.y]=start;
                }
            }
        }
        if(vis[a-1][b-1]==0)
            printf("God please help our poor hero.\n");
        else{
            printf("It takes %d seconds to reach the target position, let me show you the way.\n",vis[a-1][b-1]);
            stack<node>v;
            while(!v.empty())
                v.pop();
            start.x=a-1;start.y=b-1;start.time=vis[a-1][b-1];
            while(!(start.x==0&&start.y==0)){
                v.push(start);
                start = roate[start.x][start.y];
            }
            start.x=0,start.y=0,start.time=0;
            v.push(start);int flag=1;
            while(!(v.top().x==a-1&&v.top().y==b-1)){
                int time1=v.top().time;
                printf("%ds:(%d,%d)->",flag,v.top().x,v.top().y);
                v.pop();
                printf("(%d,%d)\n",v.top().x,v.top().y);
                flag++;
                int lzk=0;
                while(v.top().time-time1-lzk!=1){
                    printf("%ds:FIGHT AT (%d,%d)\n",flag,v.top().x,v.top().y);
                    flag++;lzk++;
                }
            }
        }printf("FINISH\n");
    }
}
這個題喃,要把VIS賦值當前所花的時間,為啥要這麼做呢,與前面那個題可不一樣,這個題其實與BFS利用佇列的原理有關係,本來是到時候通過Front()獲取的元素就應該是最早的到達終點的情況,可是打怪就破壞了這個,在沒有打怪的情況下不論怎麼走每次都是走1s,但是打怪會走多於1s,就破壞了佇列自己帶來的優勢,所以沒有用優先佇列的情況下,就要賦予VIS實際意義,每一次走過這個點就要判斷是否走過,如果走過花費的時間是不是最少,不是就更新這個情況,所以呢,需要把佇列所有都跑完直到佇列變空,大概思想就是這樣,還有一點最重要的,就是記錄路徑,這個也很複雜,我是用一個二維陣列記錄的,然後遇到一個問題,這個是倒著的,我們需要倒序輸出,這裡用了STL的Stack,利用了棧的先進先出。

詭異的樓梯

又是一個型別的題

剩下有空再寫