ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-09-11 12:32:05  阅读:173  来源: 互联网

标签:temp int 题集 ++ vis next1 双向 dir


题单

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有