其他分享
首页 > 其他分享> > 题集3(A* & 双向广搜)

题集3(A* & 双向广搜)

作者:互联网

题单

A Eight(POJ-1077)

(双向广搜 或 A*)
题目链接

首先说一下这个没有解决方案的情况,在本题中只有四种移动状态,上下左右,先看左右,他不会对改变状态后的逆序数大小进行变化,再看上下,他会对改变状态后的逆序数大小进行±2的改变(让改变的位置到x中间的2个数都加减1)或者不会对改变状态后逆序数大小进行改变(让改变的位置到x中间的2个数分别加减1),所以所有状态的奇偶性都和初始状态相同,那么我们只需要判断初始状态到最终状态(“12345678x”)的奇偶性是否相同即可,如果相同就说明有解决方案,如果不相同则说明没有解决方案。

A*:采用所有点到他实际点的哈夫曼距离之和作为预估距离,跑A*就可以了,状态回溯的话,可以采用map对每个点的上个位置进行记录,记录下走的方向及上个点,最终回溯到起点,最后进行翻转就可以了。

双向广搜:双向广搜的状态回溯和A有点不同,因为A是直接跑到了终点,所以可以直接回溯到头,在双向广搜中,分为[start, mid], [mid, mid + 1], [mid + 1, end]三个地方,所以我在回溯时传递了3个参数,分别是第一个终点(mid),然后是[mid, mid + 1]所走的方向,以及第二个起点(mid + 1)。第一次直接从mid回溯到起点再进行反转,再加上[mid, mid + 1]所走的方向,最后加上end到mid + 1的回溯就是答案,直接跑双向广搜就可以了。

A*代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;

const string str = "12345678x";
const int dir[4][2] = { {0, 1}, {0, -1}, {1, 0}, {-1, 0} };
const char DIR[4] = { 'r', 'l', 'd', 'u' };

struct PRE;

map<string, bool>vis;
map<string, PRE> pre; // 储存上一步状态

string start;

struct PRE {
	string pre;
	int dir;
	PRE(string pre, int dir) {
		this->pre = pre;
		this->dir = dir;
	}
	PRE() {}
};

struct NOW {
	string now;
	int H;
	int step;
	NOW(string now, int H, int step) {
		this->now = now;
		this->H = H;
		this->step = step;
	}
	NOW() {}
	// 重载操作符在优先队列大顶堆时 让预估距离最小的在堆顶
	bool operator< (const NOW& x) const {
		return H > x.H;
	}
}now, next1;

inline int getH(string temp) {
	// 获取哈夫曼距离
	int H = 0, x, y, num, xx, yy;
	for (int i = 0; i < 9; ++i) {
		if (temp[i] == 'x') continue;
		num = temp[i] - '1';
		x = num / 3;
		y = num % 3;
		xx = i / 3;
		yy = i % 3;
		H += abs(x - xx) + abs(y - yy);
	}
	return H;
}

inline bool judge(int x, int y) {
	if (x < 0 || x >= 3 || y < 0 || y >= 3) return true;
	return false;
}

void print() {
	// 状态回溯并进行输出
	string end = str, dir = "";
	while (end != start) {
		dir += DIR[pre[end].dir];
		end = pre[end].pre;
	}
	reverse(dir.begin(), dir.end());
	cout << dir << endl;
}

void Astar() {
	/* A* */ 
	priority_queue<NOW> que;
	que.push({ start, getH(start), 0 });
	vis[start] = true;
	while (!que.empty()) {
		int x, y, nx, ny;
		now = que.top(); que.pop();
		if (now.now == str) break;
		for (int i = 0; i < 9; ++i) {
			if (now.now[i] == 'x') {
				x = i / 3;
				y = i % 3;
				break;
			}
		}
		for (int i = 0; i < 4; ++i) {
			nx = x + dir[i][0]; ny = y + dir[i][1];
			if (judge(nx, ny)) continue;
			next1.now = now.now;
			swap(next1.now[x * 3 + y], next1.now[nx * 3 + ny]);
			if (vis[next1.now]) continue;
			vis[next1.now] = true;
			next1.step = now.step + 1;
			next1.H = getH(next1.now) + next1.step;
			pre[next1.now] = { now.now, i };
			que.push(next1);
		}
	}
	print();
}

int main() {
	string temp;
	for (int i = 0; i < 9; ++i) {
		cin >> temp;
		start += temp;
	}
	int ans = 0;
	// 求出初始逆序数大小
	for (int i = 0; i < 9; ++i) {
		if (start[i] == 'x') continue;
		for (int j = i + 1; j < 9; ++j) {
			if (start[j] == 'x') continue;
			if (start[i] > start[j]) ans++;
		}
	}
	// 判断是否与终点逆序数奇偶性相同 不相同输出无解决方案的情况
	if (ans % 2) {
		printf("unsolvable\n");
		return 0;
	}
	Astar();
	return 0;
}

双向广搜代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <map>
#include <queue>
using namespace std;

const string str = "12345678x";
const int dir[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
const char DIR[4] = { 'd', 'u', 'r', 'l' };
const char DIR2[4] = { 'u', 'd', 'l', 'r' };
struct PRE;

map<string, int> vis;
map<string, PRE> old;

string start;

struct PRE {
	int dir;
	string pre;
	PRE(int dir, string pre) {
		this->dir = dir;
		this->pre = pre;
	}
	PRE() {}
};

bool judge(int x, int y) {
	if (x < 0 || x >= 3 || y < 0 || y >= 3) return true;
	return false;
}

void print(string end1, char pre, string start1) {
	string end = end1;
	string temp = "";
	while (end != start) {
		temp += DIR[old[end].dir];
		end = old[end].pre;
	}
	reverse(temp.begin(), temp.end());
	temp += pre;
	end = start1;
	while (end != str) {
		temp += DIR2[old[end].dir];
		end = old[end].pre;
	}
	cout << temp << endl;
}

void dbfs() {
	string now1, now2, next1;
	vis[start] = 1;
	vis[str] = 2;
	queue<string> que1, que2;
	que1.push(start);
	que2.push(str);
	while (!que1.empty() && !que2.empty()) {
		int x, y, nx, ny;
		if (!que1.empty())
		{
			now1 = que1.front();
			que1.pop();
		}
		if (!que2.empty()) {
			now2 = que2.front();
			que2.pop();
		}
		for (int i = 0; i < 9; ++i) {
			if (now1[i] == 'x') {
				x = i / 3;
				y = i % 3;
				break;
			}
		}
		for (int i = 0; i < 4; ++i) {
			nx = x + dir[i][0];
			ny = y + dir[i][1];
			if (judge(nx, ny)) continue;
			next1 = now1;
			swap(next1[x * 3 + y], next1[nx * 3 + ny]);
			if (vis[next1] == 1) continue;
			else if (!vis[next1]) {
				vis[next1] = 1;
				old[next1] = { i, now1 };
				que1.push(next1);
			}
			else if (vis[next1] == 2) {
				// 曾经倒着走走到过next1
				print(now1, DIR[i], next1);
				return;
			}
		}
		for (int i = 0; i < 9; ++i) {
			if (now2[i] == 'x') {
				x = i / 3;
				y = i % 3;
				break;
			}
		}
		for (int i = 0; i < 4; ++i) {
			nx = x + dir[i][0];
			ny = y + dir[i][1];
			if (judge(nx, ny)) continue;
			next1 = now2;
			swap(next1[3 * x + y], next1[3 * nx + ny]);
			if (vis[next1] == 2) continue;
			else if (!vis[next1]) {
				vis[next1] = 2;
				old[next1] = { i, now2 };
				que2.push(next1);
			}
			else if (vis[next1] == 1) {
				// 曾经正着走走到 next1
				print(next1, DIR2[i], now2);
				// 走到next1 
				return;
			}
		}
	}
}

int main() {
	string temp;
	for (int i = 0; i < 9; ++i) {
		cin >> temp;
		start += temp;
	}
	int ans = 0;
	for (int i = 0; i < 9; ++i) {
		if (start[i] == 'x') continue;
		for (int j = i + 1; j < 9; ++j) {
			if (start[j] == 'x') continue;
			if (start[i] > start[j]) ans++;
		}
	}
	if (ans % 2) {
		printf("unsolvable\n");
		return 0;
	}
	dbfs();
	return 0;
}

B Remmarguts’ Date(POJ-2449)

(A*第k短路 + spfa + 链式前向星)
题目链接

本题题意是给出多个有向边让你求起点到终点的第k短路
我们可以先逆向利用spfa求出从终点求出终点到其余点的长度(spfa求单源最短路)
再利用A*,以当前点所走距离 + 当前点到下个点的距离 + 终点到下个点的距离作为预估长度(H) 来取最短路
当第k次到达终点时 就是答案
要注意判断-1 在终点到达起点的距离为INF时说明无法到达
还要注意判断起点和终点为同一点的情况

代码

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

const int M = 100010;
const int N = 1010;
const int INF = 0x3f3f3f3f;

int n, m, s, t, k;
int cnt = 0;
int head[N], head_end[N], dis[N];
bool vis[N];
int Vis[N];

struct Edge {
	int u, v, w, next;
}edge[M << 1];

struct EdGe {
	int v, H, step;
	EdGe(int v, int H, int step) {
		this->v = v;
		this->H = H; // H是预估距离
		this->step = step;
	}
	EdGe() {}
	bool operator< (const EdGe& x) const {
		return H > x.H;
	}
};

void init() {
	for (int i = 1; i <= n; ++i) {
		head[i] = head_end[i] = -1;
		dis[i] = INF;
	}
}

void addedge(int u, int v, int w, bool dir) {
	edge[cnt].u = u; edge[cnt].v = v; edge[cnt].w = w;
	if (dir) {
		edge[cnt].next = head[u];
		head[u] = cnt++;
	}
	else {
		edge[cnt].next = head_end[u];
		head_end[u] = cnt++;
	}
}

void spfa() {
	int now;
	queue<int>que;
	que.push(t);
	dis[t] = 0;
	while (!que.empty()) {
		now = que.front(); que.pop();
		vis[now] = false;
		for (int i = head_end[now]; i != -1; i = edge[i].next) {
			if (dis[edge[i].v] > dis[edge[i].u] + edge[i].w) {
				dis[edge[i].v] = dis[edge[i].u] + edge[i].w;
				if (vis[edge[i].v]) continue;
				que.push(edge[i].v);
				vis[edge[i].v] = true;
			}
		}
	}
}

int Astar() {
	if (dis[s] == INF) return -1;
	priority_queue <EdGe> que;
	que.push({ s, dis[s], 0 });
	EdGe now1;
	while (!que.empty()) {
		now1 = que.top(); que.pop();
		Vis[now1.v]++;
		if (Vis[now1.v] == k && now1.v == t) return now1.step;
		for (int i = head[now1.v]; i != -1; i = edge[i].next) {
			if (Vis[edge[i].v] <= k) {
				que.push({ edge[i].v, dis[edge[i].v] + now1.step + edge[i].w, now1.step + edge[i].w });
			}
		}
	}
	return -1;
}

int main() {
	int u, v, w;
	scanf("%d%d", &n, &m);
	init();
	for (int i = 0; i < m; ++i) {
		scanf("%d%d%d", &u, &v, &w);
		addedge(u, v, w, true);
		addedge(v, u, w, false);
	}
	scanf("%d%d%d", &s, &t, &k);
	if (s == t) k++;
	spfa();
	printf("%d\n", Astar());
	return 0;
}

C Nightmare Ⅱ(HDU - 3085)

(双向广搜)
题目链接

本题题意是,有两个起始点 一个每秒能移动3步,另一个每秒能移动1步,有两只鬼,每秒能移动2步,询问在鬼追上他们两个点之前,两个点是否能够碰面
本题双向广搜、

代码

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

const int M = 880;
const int dir[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };

char map1[M][M];
int vis[M][M];
int n, m, time;

struct Node {
	int x, y;
	Node(int x, int y) {
		this->x = x;
		this->y = y;
	}
	Node() {}
}start1, start2, ghost1, ghost2, now1, next1;

bool judge(Node temp) {
	if (temp.x < 0 || temp.x >= n || temp.y < 0 || temp.y >= m) return true;
	int len1 = abs(temp.x - ghost1.x) + abs(temp.y - ghost1.y), len2 = abs(temp.x - ghost2.x) + abs(temp.y - ghost2.y);
	if (time * 2 >= len1 || time * 2 >= len2) return true;
	if (map1[temp.x][temp.y] == 'X' || map1[temp.x][temp.y] == 'Z') return true;
	return false;
}

int dbfs() {
	time = 0;
	memset(vis, 0, sizeof(vis));
	queue<Node> que1, que2;
	que1.push(start1); que2.push(start2);
	vis[start1.x][start1.y] = 1; vis[start2.x][start2.y] = 2;
	while (!que1.empty() && !que2.empty()) {
		time++;
		for (int i = 0; i < 3; ++i) {
			for (int j = 0, len = que1.size(); j < len; ++j) {
				now1 = que1.front(); que1.pop();
				if (judge(now1)) continue;
				for (int k = 0; k < 4; ++k) {
					next1 = { now1.x + dir[k][0], now1.y + dir[k][1] };
					if (judge(next1)) continue;
					if (vis[next1.x][next1.y] == 1) continue;
					else if (!vis[next1.x][next1.y]) {
						vis[next1.x][next1.y] = 1;
						que1.push(next1);
					}
					else if (vis[next1.x][next1.y] == 2) return time;
				}
			}
		}
		for (int i = 0, len = que2.size(); i < len; ++i) {
			now1 = que2.front(); que2.pop();
			if (judge(now1)) continue;
			for (int j = 0; j < 4; ++j) {
				next1 = { now1.x + dir[j][0], now1.y + dir[j][1] };
				if (judge(next1)) continue;
				if (vis[next1.x][next1.y] == 2) continue;
				else if (!vis[next1.x][next1.y]) {
					vis[next1.x][next1.y] = 2;
					que2.push(next1);
				}
				else if (vis[next1.x][next1.y] == 1) return time;
			}
		}
	}
	return -1;
}

int main() {
	int t, cnt;
	scanf("%d", &t);
	while (t--) {
		cnt = 0;
		scanf("%d%d", &n, &m);
		for (int i = 0; i < n; ++i) {
			getchar();
			for (int j = 0; j < m; ++j) {
				map1[i][j] = getchar();
			}
		}
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < m; ++j) {
				if (map1[i][j] == 'M') {
					start1 = { i, j };
				}
				else if (map1[i][j] == 'G') {
					start2 = { i, j };
				}
				else if (map1[i][j] == 'Z' && cnt == 0) {
					ghost1 = { i, j };
					cnt++;
				}
				else if (map1[i][j] == 'Z' && cnt == 1) {
					ghost2 = { i, j };
				}
			}
		}
		printf("%d\n", dbfs());
	}
	return 0;
}

标签:temp,int,题集,++,vis,next1,双向,dir
来源: https://blog.csdn.net/sdtbu_jk19/article/details/120142247