Stealing Harry Potter's Precious (枚舉+ bfs)

題目鏈接

題意:在一個 N*M 的地圖中,每個格子代表一個房間,’#'代表堅不可摧的房間,是不可進入的; '.‘代表脆弱的房間🏠,是可以進入的;’@'代表起點,然後有K個寶物,分別放在了這些脆弱的房間裏面。求從起點開始,拿到所有寶物最小需要的步數(在起點步數爲0,每走一格步數+1)

錯誤思路:需要求最短路,盲猜直接BFS,將寶物的座標用一個map存起來,在struct裏面多加一個 set 來記錄當前路徑中拿到了哪些寶物,當 set.size 和 k 相等的時候,直接輸出答案就完事了。巴特,這個思路有一個不好處理的地方,在實際情況當中或許正解會同一個點走兩次,所以我們不能在剪枝的時候加上vis數組驗證,這樣就會導致大量的點來回走,並且加入隊列,耗費太多空間,就 Memory Limit Exceeded 了。

看看代碼吧🤦‍♂️:

#include <bits/stdc++.h>
using namespace std;

int n, m, k, x, y;
char mp[105][105];
map <pair<int, int>, bool> ok;
struct Node {
	int x, y, step;
	set <pair<int, int> > con;
};
queue <Node> q;

int main() {
	while(~scanf("%d %d", &n, &m) && n != 0) {
		ok.clear();
		while(!q.empty()) q.pop();
		int stax, stay;
		for(int i = 1;i <= n; i++) {
			for(int j = 1;j <= m; j++) {
				scanf(" %c", &mp[i][j]);
				if(mp[i][j] == '@') stax = i, stay = j; 
			}
		}

		
		scanf("%d", &k);
		int num = k;
		while(k--) {
			scanf("%d %d", &x, &y);
			ok[make_pair(x, y)] = 1;
		}
		
		Node t;
		t.x = stax; t.y = stay; t.step = 0;
		if(ok[make_pair(stax, stay)] == 1) {
			t.con.insert(make_pair(stax, stay));
		}
		q.push(t);
		bool fin = 0;
		int ans = -1;
		while(!q.empty()) {
			Node res = q.front(); q.pop();
			for(int i = -1;i <= 1; i++) {
				for(int j = -1;j <= 1; j++) {
					if((i != 0 && j != 0) || (i == 0 && j == 0)) continue;
					
					int tx = res.x+i, ty = res.y+j, ts = res.step+1;
					set <pair<int, int> > tc = res.con;
					if(tx > n || tx < 1 || ty > m || ty < 1 || mp[tx][ty] == '#') continue;
					Node Push;
					Push.x = tx; Push.y = ty; Push.step = ts; Push.con = tc;
					if(ok[make_pair(tx, ty)] == 1) {
						Push.con.insert(make_pair(tx, ty));
						if(num == Push.con.size()) {
							ans = ts;
							fin = 1;
							break;
						}
					}
					
					q.push(Push);
				}
				if(fin) break;
			}
			if(fin) break;
		}
		printf("%d\n", ans);
	}
}

正確思路:爲了避免上述情況,其實可以讓BFS的路徑按照我們自己想要的路線走;因爲這道題目的K值比較小,從起點開始走完所有的點的順序也就不多,四個寶物的走法也就是 43214 * 3 * 2 * 1 這麼多種,這已經是最多的情況了,把這些點可能走的路線全部走一遍,因爲bfs從一個點走到另一個點肯定是最短路徑的,把這幾段的路走的步數加起來就是一種方案,最後答案取最小的就OK了,這裏面用了一個 全排列的函數 ,也就是把從 1 ~ K可以走的全部方案枚舉出來的函數。

#include <bits/stdc++.h>
using namespace std;

int n, m, k, stax, stay;
char mp[105][105];
struct node {
	int x, y, step;
	node(int a, int b, int c): x(a), y(b), step(c){}
	node(){}
} ok[5];
int p[5];
bool meet[105][105];
int chanx[] = {-1, 1, 0, 0};
int chany[] = {0, 0, -1, 1};

int bfs() {
	queue <node> q;
	q.push(node(stax, stay, 0));
	meet[stax][stay] = 1;
	int cnt = 1;
	while(!q.empty()) {
		node t = q.front(); q.pop();
		for(int i = 0;i < 4; i++) {
			int tx = t.x + chanx[i];
			int ty = t.y + chany[i];
			int ts = t.step + 1;
			
			if(tx > n || tx < 1 || ty > m || ty < 1 || mp[tx][ty] == '#' || meet[tx][ty]) continue;
			bool flag = 0;
			if(tx == ok[p[cnt]].x && ty == ok[p[cnt]].y) flag = 1;
			if(flag) {
				while(!q.empty()) q.pop();
				memset(meet, 0, sizeof(meet));
				if(cnt == k) return ts;
				cnt++;
			}
			
			q.push(node(tx, ty, ts));
			meet[tx][ty] = 1;
			if(flag) break;
		}
	}
	return -1;
}

int main() {
	while(~scanf("%d %d", &n, &m) && n) {
		for(int i = 1;i <= n; i++) {
			for(int j = 1;j <= m; j++) {
				scanf(" %c", &mp[i][j]);
				if(mp[i][j] == '@') stax = i, stay = j;
			}
		}
		
		scanf("%d", &k);
		int num = 1;
		for(int i = 1;i <= k; i++) {
			scanf("%d %d", &ok[i].x, &ok[i].y);
			p[i] = i;
			num *= i;
		}
		
		int ans = -1;
		
		while(num--) {
			int sum = bfs();
//			cout << "sum = " << sum << endl;
			if(sum != -1) ans = ans == -1?sum:min(ans, sum);
			next_permutation(p+1, p+1+k);
		}
		printf("%d\n", ans);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章