★【搜索 】HDU 1362 Red and Black【BFS/DFS】講解

Problem Description

There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can’t move on red tiles, he can move only on black tiles.

Write a program to count the number of black tiles which he can reach by repeating the moves described above.

  • Input
    The input consists of multiple data sets. A data set starts with a line containing two positive integers W and H; W and H are the numbers of tiles in the x- and y- directions, respectively. W and H are not more than 20.

    There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows.

    ‘.’ - a black tile
    ‘#’ - a red tile
    ‘@’ - a man on a black tile(appears exactly once in a data set)

  • Output
    For each data set, your program should output a line which contains the number of tiles he can reach from the initial tile (including itself).

Sample Input

6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
11 6
..#..#..#..
..#..#..#..
..#..#..###
..#..#..#@.
..#..#..#..
..#..#..#..
7 7
..#.#..
..#.#..
###.###
...@...
###.###
..#.#..
..#.#..
0 0

Sample Output

45
59
6
13

題意:一個人站在一個長方形的房間裏,瓷磚是方形的,有紅黑兩色,這個人站在黑色瓷磚上,位置以’@‘表示,其他的黑色瓷磚用’.‘表示,紅色瓷磚用’#'表示,人可以上下左右移動到相鄰的黑色瓷磚上。題目要求我們求出這個人從初始位置能夠到達的黑色瓷磚的數量。

思路:暴力搜索整個解空間,用BFS和DFS。可以作爲【搜索技術】的入門題。

當然,可能有點難度的一點,就是圖論的建模,這道題只需把每塊瓷磚視作一個圖的一個頂點就可以了,用每塊瓷磚的座標表示。從起點出發,看起點所在的聯通分量有多少個頂點。

還有一個問題就是,如何從一個點移動到上下左右去?可以用“四連通”的寫法。

Move[][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}

解決方法一:BFS

爲了講得清楚,畫圖,以四行五列的房間爲例。BFS從起點擴散開,像一顆石子丟進池塘,激起的波浪會一層層擴散到整個空間,而且擴散按照從近到遠的順序。用隊列處理這個擴散的過程很清晰:
在這裏插入圖片描述

#include <iostream>
#include <queue>
using namespace std;
const int maxn = 25;
char room[maxn][maxn];
int Move[][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; // 左上右下
#define check(x, y) ((x < w && x >= 0) && (y < h && y >= 0))
int dx, dy;
struct point { int x, y; };
int w, h;

int bfs() {
    queue<point> q;
    int num = 1; // 記錄可走的黑色瓷磚數量
    q.push(point{dx, dy}); // 從起點開始 
	room[dy][dx] = '#'; // 把起點堵住防止重複計算, 其實可以不寫, 因爲起點是'@'而非'.' 
    while (!q.empty()) {
        point t = q.front(); q.pop();
        for (int i = 0; i < 4; ++i) {
            int tx = t.x + Move[i][0], ty = t.y + Move[i][1];
            if (check(tx, ty) && room[ty][tx] == '.') {
                ++num; // 可走的路多了一條
                q.push(point{tx, ty});
                room[ty][tx] = '#'; // 防止回退 
            }
        }
    }
    return num;
}

int main() {
    while (~scanf("%d %d", &w, &h)) {
        if (w == 0 && h == 0) break;
        for (int i = 0; i < h; ++i) {
            for (int j = 0; j < w; ++j) {
                cin >> room[i][j];
                if (room[i][j] == '@') { //找到起點座標
                    dx = j;
                    dy = i;
                }
            }
        }
        printf("%d\n", bfs());
    }
    return 0;
}

解決方法二:DFS+遞歸

也可以用棧模擬,不過這樣就和隊列+BFS的寫法幾乎完全一樣了。過程描述如下:
(1)在初始位置,num = 1,標記這個位置已經被走過;
(2)左、上、右、下4個方向,按照順序選一個能走的方向,走一步;
(3)在新的位置,num++,標記這個位置已經被走過;
(4)繼續上訴過程;
(5)如果無路可走,回退到上一步,換個方向再走;
(6)繼續上訴過程,直到結束。

畫圖如下:
在這裏插入圖片描述

#include <iostream>
#include <cstdio>
#define isLegal(x, y) (x >= 0 && x < w && y >= 0 && y < h)
using namespace std;
const int maxn = 25; 
int h, w;
int Move[][4] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; //x, y 左上右下 
char room[maxn][maxn];
int x, y; //起點的座標
int num = 0; //可以行走的黑色瓷磚的數量
 
void dfs(int x, int y) {
	room[y][x] = '#'; //標記這個位置,表示已經走過了 
	++num;
	for (int i = 0; i < 4; ++i) { //在左上右下四個方向順時針深搜
		int nextX = x + Move[i][0], nextY = y + Move[i][1];
		if (isLegal(nextX, nextY) && room[nextY][nextX] == '.') //選一個能走的方向
			dfs(nextX, nextY); 
	}
}

int main() {
	while (~scanf("%d%d", &w, &h)) { //寬和高 列和行
		if (!w && !h) break;
		for (int i = 0; i < h; ++i) {
			for (int j = 0; j < w; ++j) {
				cin >> room[i][j];
				if (room[i][j] == '@') x = j, y = i; //找到起點座標
			}
		}
		dfs(x, y);
		cout << num << endl;
		num = 0;
	}
	return 0; 
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章