A - Eight HDU - 1043(A*算法、双向bfs)
题意
- 给我们 8个数(1,23…)和一个字母x组成的一个序列,让我们通过 上下左右移动x操作,把这个序列变成:1 2 3 4 5 6 7 8 x
思路一
- 我们先考虑,什么是 双向bfs ?,简单的说就是,从起点、终点两个方向开始 bfs搜索,
- 这个算法的实现是通过
- 两个队列
q1、q2, 我们分别把起点、终点的搜索状态 压入
q1、q2,
- 再通过
vis1[ ][ ]、vis2[ ][ ] 数组分别 标记 q1 所代表的正向bfs搜索中 所走过的点、标记 q1 所代表的正向bfs搜索中 所走过的点。
- 我们进行具体搜索的时候(就是再代码实现中while(q1 != emtpy && q2 != empty)的内部),我们分别从 q1、q2中各取出一个元素(设为a、b),先对第一个元素a进行bfs搜索,如果在搜索到某个位置、状态的时候,而这个位置、状态已经在vis2[][]中出现了,那么就完成了搜索任务;否则的再进行都b的bfs搜索若果它再bfs搜索过程中到的某个位置、状态再vis1[][]中出现过的话,那么ok也完成了搜索的任务,可以输出ans了,,,如果没有的话,就再从q1、q2中再各取一个元素,再进行搜索,,,这样一个循环下去就能得到ans。。
- 还有要注意细节,两个bfs循环中的 ,第一个bfs搜索的 位置改变是正着的,而第二个bfs 位置改变我们 记录成 逆向的 (如x—>y,我在记录路径的时候:记录成 y–>x)
- 具体的到这个题中要进行的,
- 首先我们要考虑怎么记录每一个
搜索状态
,这里要用到 康托展开,他是干什么的,就是对于每一个 序列(元素不重复),我们可以 给他一个 编号,而这个『编号』,我们可以通过 vis[编号] = 1 来记录已经搜索过的状态,
- 记录搜索的路径,这里我们通过 并查集 来记录的搜的路径,在记录路径的时候,我们一定要保证,路径所代表的下表是唯一的(不能用康托展开对每个状态产生的编号),具体实现在 代码中 多注意!!!
- 我们还提前判断的 刚开始所给序列的逆序数的数量(设为ct),如果 ct 为偶数则有可能有ans;否则一定没有ans
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<string>
#include<cstdio>
#include<cmath>
#include<stack>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); } void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define INF 0x3f3f3f3f
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (s); i <= (e); i ++)
#define rep_(i, e, s) for(int i = (e); i >= (s); i --)
#define sd(a) scanf("%d", &a)
#define sc(a) scanf("%c", &a)
using namespace std;
const int N = 10;
const int mxn = 5e5 + 10;
int mov[4] = { -3, 3, -1, 1 };
int mul[N] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
int vis1[mxn];
int vis2[mxn];
char d1[5] = "udlr";
char d2[5] = "durl";
struct Node
{
int a[N];
int p;
} S1, S2;
struct Path
{
int num;
char ch;
} ph[mxn];
int contor(int a[N])
{
int res = 0;
for_(i, 1, 9)
{
int ct = 0;
for_(j, 1, i - 1)
if(a[i] < a[j])
ct ++;
res += ct * mul[i - 1];
}
return res;
}
void print(int x)
{
if(ph[x].num == -1) return;
print(ph[x].num);
printf("%c", ph[x].ch);
}
bool good(int i, int p)
{
if(i == 0 && p < 4) return false;
if(i == 1 && p > 6) return false;
if(i == 2 && p % 3 == 1) return false;
if(i == 3 && p % 3 == 0) return false;
return true;
}
void bfs()
{
ph[1].num = -1;
vis1[contor(S1.a)] = 1;
ph[2].num = -1;
vis2[contor(S2.a)] = 2;
queue<Node> q1, q2;
q1.push(S1);
q2.push(S2);
int n = 2;
while(! q1.empty() && ! q2.empty())
{
Node T1 = q1.front(); q1.pop();
int ct_pre = contor(T1.a);
if(vis2[ct_pre])
{
print(vis1[ct_pre]);
int x = vis2[ct_pre];
while(ph[x].num != -1)
{
printf("%c", ph[x].ch);
x = ph[x].num;
}
printf("\n");
return;
}
for_(i, 0, 3)
{
Node now = T1;
if(! good(i, now.p)) continue;
swap(now.a[now.p], now.a[now.p + mov[i]]);
now.p += mov[i];
int ct_now = contor(now.a);
if(vis1[ct_now]) continue;
vis1[ct_now] = ++ n;
ph[n].num = vis1[ct_pre];
ph[n].ch = d1[i];
q1.push(now);
}
Node T2 = q2.front(); q2.pop();
ct_pre = contor(T2.a);
if(vis1[ct_pre])
{
print(vis1[ct_pre]);
int x = vis2[ct_pre];
while(ph[x].num != -1)
{
printf("%c", ph[x].ch);
x = ph[x].num;
}
printf("\n");
return;
}
for_(i, 0, 3)
{
Node now = T2;
if(! good(i, now.p)) continue;
swap(now.a[now.p], now.a[now.p + mov[i]]);
now.p += mov[i];
int ct_now = contor(now.a);
if(vis2[ct_now]) continue;
vis2[ct_now] = ++ n;
ph[n].num = vis2[ct_pre];
ph[n].ch = d2[i];
q2.push(now);
}
}
printf("unsolvable\n");
}
void init()
{
for_(i, 1, 9) S2.a[i] = i % 9;
S2.p = 9;
}
int main()
{
char ch;
init();
while(sc(ch) != EOF)
{
for_(i, 1, 9)
{
if(i != 1) sc(ch);
if(ch == 'x')
S1.a[i] = 0, S1.p = i;
else
S1.a[i] = ch - '0';
getchar();
}
int k = 0;
for_(i, 1, 9)
{
if(S1.a[i] == 0) continue;
for_(j, i + 1, 9)
{
if(S1.a[j] == 0) continue;
if(S1.a[i] > S1.a[j]) k ++;
}
}
memset(vis1, 0, sizeof(vis1));
memset(vis2, 0, sizeof(vis2));
if(k & 1) printf("unsolvable\n");
else bfs();
}
return 0;
}
思路二
- 对这个的算法我的理解:首先不要把这个算法看的太难(其实思路很简单)
- 我们一开始可以把他当成bfs搜索,不过这个搜的队列 是priority_queue 来实现的,既然是优先级的列的那就要 按照的优先级的 高低 来决定的搜索的顺序,
- 那么对于任意一个搜索位置、状态 我们怎么确定的它的优先级的大小呢?这里我们通过引入 估值函数
优先级f=g+h,在迷宫问题中g为从最初的搜索的状态(起点)到当前正在讨论的搜索状态,走了多少步/用了多少时间;h为桶当前正在讨论的搜索状态->到目标搜索的状态(终点)所需要的 步数/时间;g+h就得到了 每个状态(正在搜索的位置)的优先级 f,通过f 就可在 priority_queue 中自动排序了,按优先级高(这里的优先级高指的是 f值越小)的搜索了,这样就可 一直搜索就能的得到的ans了,
注意上面的 步数/时间 在A* 算法中一般指的是 曼哈顿距离(横、纵坐标的差的绝对值之和)
- 对于这一题,记录状态 时的下标我们可以直接中 康托展开产生的编号 坐下标(注意:为什的在这个算法中 可以 用它做下标,而在第一个中却不可以)
- 记录路 还是用并查集
- 还要判断 初始序列的数的数量
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<map>
#include<string>
#include<cstdio>
#include<cmath>
#include<stack>
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); } void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define db double
#define Pir pair<int, int>
#define PIR pair<Pir, Pir>
#define INF 0x3f3f3f3f
#define mod (ll)(1e9 + 7)
#define for_(i, s, e) for(int i = (s); i <= (e); i ++)
#define rep_(i, e, s) for(int i = (e); i >= (s); i --)
#define sd(a) scanf("%d", &a)
#define sc(a) scanf("%c", &a)
using namespace std;
const int N = 10;
const int mxn = 5e5 + 10;
int mov[4] = { -3, 3, -1, 1 };
int mul[N] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320 };
char dir[N] = "udlr";
int vis[mxn];
Pir ps[N];
struct Node
{
int a[N];
int p;
int g, h;
int idx;
bool operator < (const Node x) const
{
return h + g > x.h + x.g;
}
} s;
struct Path
{
int idx;
char ch;
} ph[mxn];
void print(int x)
{
if(ph[x].idx == -1) return;
print(ph[x].idx);
printf("%c", ph[x].ch);
}
bool good(int i, int p)
{
if(i == 0 && p < 4) return false;
if(i == 1 && p > 6) return false;
if(i == 2 && p % 3 == 1) return false;
if(i == 3 && p % 3 == 0) return false;
return true;
}
int contor(int a[N])
{
int res = 0;
for_(i, 1, 9)
{
int ct = 0;
for_(j, 1, i - 1)
if(a[i] < a[j])
ct ++;
res += ct * mul[i - 1];
}
return res;
}
int get_h(int a[N])
{
int h = 0;
for_(i, 0, 2)
for_(j, 1, 3)
h += abs(ps[a[i * 3 + j]].fi - (i + 1)) + abs(ps[a[i * 3 + j]].se - j);
return h;
}
void A_star()
{
memset(vis, 0, sizeof(vis));
int ct = contor(s.a);
vis[ct] = 1;
ph[ct].idx = -1;
s.idx = ct;
s.g = 0;
s.h = get_h(s.a);
priority_queue<Node> q;
q.push(s);
while(! q.empty())
{
s = q.top(); q.pop();
if(s.h == 0)
{
print(s.idx);
printf("\n");
return;
}
for_(i, 0, 3)
{
Node t = s;
if(! good(i, t.p)) continue;
swap(t.a[t.p], t.a[t.p + mov[i]]);
t.idx = contor(t.a);
if(vis[t.idx]) continue;
vis[t.idx] = 1;
t.p += mov[i];
t.g ++;
t.h = get_h(t.a);
ph[t.idx].idx = s.idx;
ph[t.idx].ch = dir[i];
q.push(t);
}
}
printf("unsolvable\n");
}
int main()
{
ps[1].fi = 1, ps[1].se = 1;
ps[2].fi = 1, ps[2].se = 2;
ps[3].fi = 1, ps[3].se = 3;
ps[4].fi = 2, ps[4].se = 1;
ps[5].fi = 2, ps[5].se = 2;
ps[6].fi = 2, ps[6].se = 3;
ps[7].fi = 3, ps[7].se = 1;
ps[8].fi = 3, ps[8].se = 2;
ps[0].fi = 3, ps[0].se = 3;
char ch;
while(sc(ch) != EOF)
{
for_(i, 1, 9)
{
if(i != 1) sc(ch);
if(ch == 'x')
s.a[i] = 0, s.p = i;
else
s.a[i] = ch - '0';
getchar();
}
int k = 0;
for_(i, 1, 9)
{
if(s.a[i] == 0) continue;
for_(j, i + 1, 9)
{
if(s.a[j] == 0) continue;
if(s.a[i] > s.a[j]) k ++;
}
}
if(k & 1) printf("unsolvable\n");
else A_star();
}
return 0;
}