题集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