POJ 3026 Borg Maze【解題報告|DFS+PRIM】

題目鏈接

題目描述

在一個 y行 x列 的迷宮中,有可行走的通路空格  ,不可行走的牆 #,還有兩種英文字母 A 和 S ,現在從 S 出發,要求用最短的路徑 L 連接所有字母,輸出這條路徑 L 的總長度。

思路分析

BFS + Prim

一格的長度爲1,而且移動的方法只有上、下、左、右,

所以在無任何牆的情況下(但“牆#”是必須考慮的,這裏只是爲了說明)

任意兩個字母之間的距離就是直接把 橫座標之差 加上 縱座標之差

注意的是:
① 可行的路爲 字母 和 空格
② 不可行的路爲 # 和 矩陣範圍之外

根據題意的**“分離”規則**,重複走過的路不再計算

因此當使用prim算法求L的長度時,根據算法的特徵恰好不用考慮這個問題(源點合併很好地解決了這個問題),L就是最少生成樹的總權值W

由於使用prim算法求在最小生成樹,因此無論哪個點做起點都是一樣的,(通常選取第一個點),因此起點不是S也沒有關係

所以所有的A和S都可以一視同仁,看成一模一樣的頂點就可以了

最後要注意的就是 字符的輸入:

cin不讀入空字符(包括 空格,換行等)
gets讀入空格,但不讀入換行符)

剩下的問題關鍵就是處理 任意兩字母間的最短距離,由於存在了“牆#” ,這個距離不可能單純地利用座標加減去計算,必須額外考慮,推薦用BFS(廣搜、寬搜),這是本題的唯一難點,因爲prim根本直接套用就可以了。

另外注意在求 任意兩字母間的最短距離 時 不能直接 用 BFS:

(1) 必須先把矩陣中每一個允許通行的格看做一個節點(就是在矩陣內所有非#的格都作爲圖M的一個頂點),對每一個節點i,分別用BFS求出它到其他所有節點的權值(包括其本身,爲0),構造節點圖M;
(2) 然後再加一個判斷條件,從圖M中抽取以字母爲頂點的圖,進而構造字母圖N。這個判定條件就是當節點圖M中的某點j爲字母時,把i到j的權值再複製(不是抽離)出來,記錄到字母圖N的鄰接矩陣中
(3) 剩下的就是對字母圖N求最小生成樹了

//252K	47MS
#define inf 0x3f3f3f3f
#define ll long long
#define vec vector<int>
#define P pair<int,int>
#define MAX 55

int T, m, n, cnt, ma[MAX][MAX], dis[105][105], vis[MAX][MAX];
int dx[4] = { 1,-1,0,0 }, dy[4] = { 0,0,1,-1 }, mind[105], belong[105];
string s[MAX];

void bfs(int x, int y,int id) {
	fill(dis[id], dis[id] + 105, inf);
	memset(vis, -1, sizeof(vis));
	dis[id][id] = 0; queue<P> q; q.push(P(x, y)); vis[x][y] = 0;
	while (!q.empty()) {
		x = q.front().first, y = q.front().second; q.pop();
		for (int i = 0; i < 4; i++) {
			int xx = x + dx[i], yy = y + dy[i];
			if (xx < 0 || xx >= m || yy < 0 || yy >= n ||
				s[xx][yy] == '#' || vis[xx][yy] != -1)continue;
			vis[xx][yy] = vis[x][y] + 1; q.push(P(xx, yy));
			if (ma[xx][yy] > 0) //這是一個點
				dis[id][ma[xx][yy]] = vis[xx][yy];//更新到他的距離
		}
	}
}

//從s開始的最小生成樹
int prim(int s) {
	memset(belong, 0, sizeof(belong)); belong[s] = 1;
	for (int i = 1; i <= cnt; i++) mind[i] = dis[s][i];

	int res = 0;
	//依次找到加入的cnt-1個點
	for (int i = 1; i < cnt; i++) {
		int mi = inf, id = -1;
		//找到離當前點集最近的點
		for (int j = 1; j <= cnt; j++)
			if (mind[j] < mi && !belong[j])
				mi = mind[j], id = j;

		if (id == -1)break;
		belong[id] = 1; res += mind[id];
		for (int j = 1; j <= cnt; j++)
			if (!belong[j] && mind[j] > dis[id][j])
				mind[j] = dis[id][j];
	}
	return res;
}

int main() {
	cin >> T;
	while (T--) {
		cin >> n >> m;
		char temp[51];
		gets_s(temp);
		for (int i = 0; i < m; i++)getline(cin, s[i]);
		
		cnt = 1;
		//給每個A一個編號
		for (int i = 0; i < m; i++) 
			for (int j = 0; j < s[i].size(); j++) {
				if (s[i][j] == 'A')ma[i][j] = ++cnt;
				else if (s[i][j] == 'S')ma[i][j] = 1;
				else ma[i][j] = 0;
			}
		
		//計算每個A到其他節點的距離
		for (int i = 0; i < m; i++) 
			for (int j = 0; j < s[i].size(); j++) {
				if (ma[i][j] == 0)continue;
				bfs(i, j, ma[i][j]);
			}
		
		cout << prim(1) << endl;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章