1. 程式人生 > 其它 >初學演算法----深度優先搜尋地圖型題目

初學演算法----深度優先搜尋地圖型題目

Maze

描述:

Acm, a treasure-explorer, is exploring again. This time he is in a special maze, in which there are some doors (at most 5 doors, represented by 'A', 'B', 'C', 'D', 'E' respectively). In order to find the treasure, Acm may need to open doors. However, to open a door he needs to find all the door's keys (at least one) in the maze first. For example, if there are 3 keys of Door A, to open the door he should find all the 3 keys first (that's three 'a's which denote the keys of 'A' in the maze). Now make a program to tell Acm whether he can find the treasure or not. Notice that Acm can only go up, down, left and right in the maze.

輸入:

The input consists of multiple test cases. The first line of each test case contains two integers M and N (1 < N, M < 20), which denote the size of the maze. The next M lines give the maze layout, with each line containing N characters. A character is one of the following: 'X' (a block of wall, which the explorer cannot enter), '.' (an empty block), 'S' (the start point of Acm), 'G' (the position of treasure), 'A', 'B', 'C', 'D', 'E' (the doors), 'a', 'b', 'c', 'd', 'e' (the keys of the doors). The input is terminated with two 0's. This test case should not be processed.

輸出:

For each test case, in one line output "YES" if Acm can find the treasure, or "NO" otherwise.

樣例輸入:

4 4 
S.X. 
a.X. 
..XG 
.... 
3 4 
S.Xa 
.aXB 
b.AG 
0 0

樣例輸出:

YES 
NO

1.關於找到結果就回歸這件事:
像這種在圖上遍歷找某個確定的點,找到點後,可以不用再找了,直接退出整個遍歷過程輸出即可,
那麼為了使達到這種效果,我們就要設定一點套路讓他去能夠一找到就退出整個過程

bool dfs (int y,int x)
{
if (maper[y][x]=='G')
{
return true;
}
if (maper[y][x]=='X' || x<1 || y<1 || x>c || y>r || vis[y][x]==1)
{
return false;
}
if (maper[y][x]>='a' && maper[y][x]<='e')
{
keyer[maper[y][x]-'a']++;
}
if (maper[y][x]>='A' && maper[y][x]<='E')
{
if (keyer[maper[y][x]-'A']!=needkey[maper[y][x]-'A'])
{
return false;
}
}
vis[y][x]=1;
for (int i=0;i<4;i++)
{
int ny=y+dy[i],nx=x+dx[i];
if (dfs(ny,nx))//看這部操作,如果我找到了,深度優先搜尋返回正確,那一定是一直返回到最終,就可以達到那種效果了


{
return true;
}
}
vis[y][x]=0;
return false;
}

當然這種操作有很多方式,根據具體情況具體設定

2.關於如何回溯與判斷條件寫哪裡這件事:

1.bool dfs(int y, int x, int step) { if (step == r * c) { return true; } for (int i = 0; i < 8; i++) { int ny = y + dy[i]; int nx = x + dx[i]; if (nx < 1 || ny < 1 || nx > c || ny > r || vis[ny][nx] == 1) { continue; } else { vis[ny][nx] = 1; step++; maper[step].row = ny; maper[step].col = nx; if (dfs(ny, nx, step)) { return true; } step--; vis[ny][nx] = 0; } } return false; } 2.

bool dfs (int y,int x)
{
if (maper[y][x]=='G')
{
return true;
}
if (maper[y][x]=='X' || x<1 || y<1 || x>c || y>r || vis[y][x]==1)
{
return false;
}
if (maper[y][x]>='a' && maper[y][x]<='e')
{
keyer[maper[y][x]-'a']++;
}
if (maper[y][x]>='A' && maper[y][x]<='E')
{
if (keyer[maper[y][x]-'A']!=needkey[maper[y][x]-'A'])
{
return false;
}
}
vis[y][x]=1;
for (int i=0;i<4;i++)
{
int ny=y+dy[i],nx=x+dx[i];
if (dfs(ny,nx))
{
return true;
}
}
vis[y][x]=0;
return false;
}

我這兩段程式碼判斷條件放的位置不一樣

他們其實都是同一個道理

接下來是上述題目了:

#include <bits/stdc++.h> using namespace std;
char maper[25][25]; int r, c; int keyer[5], needkey[5], everhave[5]; int vis[25][25], dy[] = {-1, 0, 1, 0}, dx[] = {0, 1, 0, -1};
bool dfs(int y, int x) { if (maper[y][x] == 'G') { return true; } for (int i = 0; i < 4; i++) { int ny = y + dy[i], nx = x + dx[i]; if (maper[ny][nx] == 'X' || nx < 1 || ny < 1 || nx > c || ny > r || vis[ny][nx] == 1) { continue; } else if (maper[ny][nx] >= 'A' && maper[ny][nx] <= 'E') { if (keyer[maper[ny][nx] - 'A'] != needkey[maper[ny][nx] - 'A']) { continue; } } else if (maper[ny][nx] >= 'a' && maper[ny][nx] <= 'e' && everhave[maper[ny][nx] - 'a'] == 0) { keyer[maper[ny][nx] - 'a']++; everhave[maper[ny][nx] - 'a'] = 1; } vis[ny][nx] = 1; if (dfs(ny, nx)) { return true; } vis[ny][nx] = 0; } return false; }
int main() { while (1) { cin >> r >> c; if (r == 0 && c == 0) { break; } else { int sx, sy; for (int i = 1; i <= r; i++) { for (int j = 1; j <= c; j++) { cin >> maper[i][j]; if (maper[i][j] == 'S') { sy = i; sx = j; } else if ('a' <= maper[i][j] && 'e' >= maper[i][j]) { needkey[maper[i][j] - 'a']++; } } } memset(vis, 0, sizeof(vis)); memset(keyer, 0, sizeof(keyer)); memset(needkey, 0, sizeof(needkey)); memset(everhave, 0, sizeof(everhave)); vis[sy][sx] = 1; if (dfs(sy, sx)) { printf("YES\n"); } else { printf("NO\n"); } } } return 0; } 次林夢葉21:54:57
我這段程式碼肯定沒錯,但是一定超時,為什麼?因為回溯實在是太多了。 想想回溯的作用是什麼?是防止退回後,再次搜尋時,本應該要到達的點,因為上一次沒有回溯而到不了

次林夢葉21:55:43
這裡走迷宮,會超時是因為走太多次相同的點了

次林夢葉21:55:53
如果我想每個點我只走一次

次林夢葉21:56:05
那我就應該不回溯

次林夢葉21:56:28
不回溯的結果就是我不能重複走點了

次林夢葉21:56:34
但是依舊能夠回退

次林夢葉21:56:38
選擇能走的點走

次林夢葉21:56:46
這樣就可以大大減少時間了

來看下大佬的寫法: #include<iostream>
#include<cstring>
using namespace std;
char a[30][30];int c[30][30],d[30][30];
int b[5],bb[5],cx[4]={1,-1},cy[4]={0,0,1,-1};
int m,n,x1,y1,f;
void ys(int x,int y)
{
if(f)return;//看這裡,大佬找到結果後立馬退出整個過程的方法
for(int i=0;i<4;i++)
{
int x2=x+cx[i],y2=y+cy[i];
if(a[x2][y2]=='G')//如果找鑰匙時找到寶藏,做標記,然後直接結束所有過程。
{
f=1;
return;
}
if(a[x2][y2]>='a'&&a[x2][y2]<='e')
{
b[a[x2][y2]-'a']--;//每找到一個鑰匙,對應的b陣列自減,值為0時代表全部集齊
a[x2][y2]='.';//將被找到的鑰匙標記為空地
}
if(a[x2][y2]=='.'&&!c[x2][y2])
{
c[x2][y2]=1; //標記找鑰匙時走過的路
ys(x2,y2);
}//由於找鑰匙時只要把所有路徑走一遍,所以不需要回溯。
}
}

void ljq(int x,int y)
{
if(f)return;//找到寶藏後直接結束
for(int i=0;i<4;i++)
{
int x2=x+cx[i],y2=y+cy[i];
if(a[x2][y2]>='A'&&a[x2][y2]<='E'&&!b[a[x2][y2]-'A']&&bb[a[x2][y2]-'A'])
{ //找到門後,如果已經集齊全部鑰匙,則把門開啟
a[x2][y2]='.';//將被開啟的門標記為空地
memset(d,0,sizeof(d));/*d陣列紀錄找門時走過的路,由於會找到新的鑰匙,可能原來沒打
開的門可以開啟,所以將d陣列清零。*/
ys(x2,y2);//每開啟一個門,從被開啟門的位置開始找鑰匙。
}

if(a[x2][y2]=='.'&&!d[x2][y2])
{
d[x2][y2]=1;
ljq(x2,y2);
}//同上 ,不需要回溯。
}
}
int main()
{
cin>>m>>n;
while(m)
{
f=0;
memset(a,0,sizeof(a));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
memset(b,0,sizeof(b));
memset(bb,0,sizeof(bb));
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
{
cin>>a[i][j];
if(a[i][j]=='S')
{
x1=i;y1=j;
a[i][j]='.';
}
if(a[i][j]>='a'&&a[i][j]<='e')
{
b[a[i][j]-'a']++;
bb[a[i][j]-'a']=1;
}//b陣列紀錄每種鑰匙的數目,bb陣列紀錄有沒有出現這種鑰匙,0下標代表a,依此類推

}
ys(x1,y1);
/*次林夢葉22:00:31
先在能走的地方把鑰匙都找完

次林夢葉22:00:58
而不是邊找鑰匙找到鑰匙就立馬去開門

次林夢葉22:01:00
那樣會很麻煩

次林夢葉22:01:34
然後看一下能不能開門,如果開不了門就一定找不到寶藏*/
ljq(x1,y1);
if(f)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
cin>>m>>n;
}
}