题目:
http://acm.hdu.edu.cn/showproblem.php?pid=1043
http://poj.org/problem?id=1077
题意:
经典8数码问题,求出任意一个操作过程
思路:
首先进行逆序数判断,可以发现,在八数码中,交换两个相连的位置,整个八数码中逆序数的数量的奇偶性不变,而终态的逆序数的奇偶性是偶,所以开始时检查初始状态逆序数的奇偶性,为奇的话则无解。然后求解操作过程,直接bfs的话,poj是可以过的,hdu是明显不能过的,但我们发现终点是唯一的,可以从终点出发进行一次逆向的bfs,把到达其他所有点的过程都记录下来,这样就是打表,以后直接查询即可,在hdu上效率是极快的,100ms左右。双向bfs直接搜的话,1000ms左右。用A*的话,效率跟估价函数的设计有关,定义g(n)为已走过的步数,h(n)为每个位置上的数字与终态相应位置上的数字的曼哈顿距离,f(n)=g(n)+h(n),直接用f(n)的话,效率1000ms左右,先h(n)在g(n)的话,效率500ms左右
逆向bfs:
//109ms
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 10, M = 500000 + 10, INF = 0x3f3f3f3f;
struct node
{
int val, idx;
int a[N];
};
int fact[N], pre[M];
char op[M], str[M];
bool vis[M];
int dir[] = {-3, 3, -1, 1};
char dirc[] = {'d', 'u', 'r', 'l'};
void fact_table(int n)
{
fact[0] = 1;
for(int i = 1; i <= n; i++) fact[i] = fact[i-1] * i;
}
int Cantor_expansion(int *s, int n)
{
int str = 0;
for(int i = 1; i <= n; i++)
{
int rnk = 0;
for(int j = i+1; j <= n; j++)
if(s[i] > s[j]) rnk++;
str += rnk * fact[n-i];
}
return str;
}
bool check(int a[], int n)
{
int tot = 0;
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++)
if(a[i] != 9 && a[j] != 9 && a[i] > a[j]) tot++;
return !(tot&1);
}
void bfs(node s)
{
queue<node> que;
memset(vis, 0, sizeof vis);
memset(pre, -1, sizeof pre);
que.push(s), vis[s.val] = true;
while(! que.empty())
{
node p = que.front(); que.pop();
for(int i = 0; i < 4; i++)
{
if((p.idx == 1 || p.idx == 4 || p.idx == 7) && i == 2) continue;
if((p.idx == 3 || p.idx == 6 || p.idx == 9) && i == 3) continue;
int x = p.idx + dir[i];
if(x >= 1 && x <= 9)
{
swap(p.a[p.idx], p.a[x]);
int val = Cantor_expansion(p.a, 9);
if(! vis[val])
{
vis[val] = true;
pre[val] = p.val;
op[val] = dirc[i];
swap(val, p.val);
swap(x, p.idx);
que.push(p);
swap(val, p.val);
swap(x, p.idx);
}
swap(p.a[p.idx], p.a[x]);
}
}
}
}
int main()
{
int a[N];
fact_table(10);
node p;
p.idx = 9;
for(int i = 1; i <= 9; i++) p.a[i] = i;
p.val = Cantor_expansion(p.a, 9);
bfs(p);
while(gets(str))
{
int k = 0;
for(int i = 0; str[i]; i++)
{
if(str[i] == 'x') a[++k] = 9;
else if(str[i] >= '0' && str[i] <= '9') a[++k] = str[i] - '0';
}
if(! check(a, 9))
{
puts("unsolvable"); continue;
}
k = 0;
int en = Cantor_expansion(a, 9);
while(pre[en] != -1)
{
str[k++] = op[en];
en = pre[en];
}
str[k] = '\0';
puts(str);
}
return 0;
}
双向bfs:
//800ms
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 10, M = 500000 + 10, INF = 0x3f3f3f3f;
struct node
{
int val, idx;
int a[N];
};
int fact[N], pre[M];
char op[M], str[M];
int vis[M];
int dir[] = {-3, 3, -1, 1};
char dirc[] = {'u', 'd', 'l', 'r'};
char dirr[] = {'d', 'u', 'r', 'l'};
void fact_table(int n)
{
fact[0] = 1;
for(int i = 1; i <= n; i++) fact[i] = fact[i-1] * i;
}
int Cantor_expansion(int *s, int n)
{
int str = 0;
for(int i = 1; i <= n; i++)
{
int rnk = 0;
for(int j = i+1; j <= n; j++)
if(s[i] > s[j]) rnk++;
str += rnk * fact[n-i];
}
return str;
}
bool check(int a[], int n)
{
int tot = 0;
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++)
if(a[i] != 9 && a[j] != 9 && a[i] > a[j]) tot++;
return !(tot&1);
}
void bfs(node s, node t)
{
queue<node> que;
memset(vis, 0, sizeof vis);
memset(pre, -1, sizeof pre);
que.push(s), vis[s.val] = 1;
que.push(t), vis[t.val] = 2;
while(! que.empty())
{
node p = que.front(); que.pop();
for(int i = 0; i < 4; i++)
{
if((p.idx == 1 || p.idx == 4 || p.idx == 7) && i == 2) continue;
if((p.idx == 3 || p.idx == 6 || p.idx == 9) && i == 3) continue;
int x = p.idx + dir[i];
if(x >= 1 && x <= 9)
{
swap(p.a[p.idx], p.a[x]);
int val = Cantor_expansion(p.a, 9);
if(! vis[val])
{
vis[val] = vis[p.val];
pre[val] = p.val;
op[val] = (vis[p.val] == 1) ? dirc[i] : dirr[i];
swap(val, p.val);
swap(x, p.idx);
que.push(p);
swap(val, p.val);
swap(x, p.idx);
}
else if(vis[val] != vis[p.val])
{
int k = 0;
if(vis[val] == 1)
{
int en = val;
while(pre[en] != -1) str[k++] = op[en], en = pre[en];
reverse(str, str + k);
str[k++] = (vis[p.val] == 1) ? dirc[i] : dirr[i];
en = p.val;
while(pre[en] != -1) str[k++] = op[en], en = pre[en];
str[k++] = '\0';
}
else
{
int en = p.val;
while(pre[en] != -1) str[k++] = op[en], en = pre[en];
reverse(str, str + k);
str[k++] = (vis[p.val] == 1) ? dirc[i] : dirr[i];
en = val;
while(pre[en] != -1) str[k++] = op[en], en = pre[en];
str[k++] = '\0';
}
return;
}
swap(p.a[p.idx], p.a[x]);
}
}
}
}
int main()
{
int a[N];
fact_table(10);
while(gets(str))
{
int k = 0, idx;
for(int i = 0; str[i]; i++)
{
if(str[i] == 'x') a[++k] = 9, idx = k;
else if(str[i] >= '0' && str[i] <= '9') a[++k] = str[i] - '0';
}
if(! check(a, 9))
{
puts("unsolvable"); continue;
}
node p, t;
p.idx = idx;
memcpy(p.a, a, sizeof a);
p.val = Cantor_expansion(p.a, 9);
t.idx = 9;
for(int i = 1; i <= 9; i++) t.a[i] = i;
t.val = Cantor_expansion(t.a, 9);
bool flag = true;
for(int i = 1; i <= 9; i++)
if(p.a[i] != t.a[i]) flag = false;
if(flag) puts("");//起点终点相同时,双向bfs无法求解,这里特判一下
else
{
bfs(p, t);
puts(str);
}
}
return 0;
}
A∗搜索:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 10, M = 500000 + 10, INF = 0x3f3f3f3f;
struct node
{
int val, idx;
int g, h;
int a[N];
friend bool operator< (node ta, node tb)
{
//return ta.g + ta.h > tb.g + tb.h; //1000ms
if(ta.h != tb.h) return ta.h > tb.h;//500ms
else return ta.g > tb.g;
}
};
int fact[N], pre[M];
char op[M], str[M];
bool vis[M];
int dir[] = {-3, 3, -1, 1};
char dirc[] = {'u', 'd', 'l', 'r'};
void fact_table(int n)
{
fact[0] = 1;
for(int i = 1; i <= n; i++) fact[i] = fact[i-1] * i;
}
int Cantor_expansion(int *s, int n)
{
int str = 0;
for(int i = 1; i <= n; i++)
{
int rnk = 0;
for(int j = i+1; j <= n; j++)
if(s[i] > s[j]) rnk++;
str += rnk * fact[n-i];
}
return str;
}
bool check(int a[], int n)
{
int tot = 0;
for(int i = 1; i <= n; i++)
for(int j = i+1; j <= n; j++)
if(a[i] != 9 && a[j] != 9 && a[i] > a[j]) tot++;
return !(tot&1);
}
int get_h(node &p)
{
int ans = 0;
for(int i = 1; i <= 9; i++)
ans += abs((i-1)/3 - (p.a[i]-1)/3) + abs((i-1)%3 - (p.a[i]-1)%3);
return ans;
}
void a_star(node s, node t)
{
priority_queue<node> que;
memset(pre, -1, sizeof pre);
memset(vis, 0, sizeof vis);
s.g = 0, s.h = get_h(s);
que.push(s), vis[s.val] = true;
node p, tp;
while(! que.empty())
{
p = que.top(); que.pop();
if(p.val == t.val) return;
for(int i = 0; i < 4; i++)
{
if((p.idx == 1 || p.idx == 4 || p.idx == 7) && i == 2) continue;
if((p.idx == 3 || p.idx == 6 || p.idx == 9) && i == 3) continue;
int x = p.idx + dir[i];
if(x >= 1 && x <= 9)
{
tp = p;
swap(tp.a[x], tp.a[tp.idx]);
int val = Cantor_expansion(tp.a, 9);
if(! vis[val])
{
vis[val] = true;
pre[val] = p.val;
op[val] = dirc[i];
tp.idx = x, tp.val = val;
tp.h = get_h(tp), tp.g++;
que.push(tp);
}
}
}
}
}
int main()
{
int a[N];
fact_table(10);
node t;
t.idx = 9;
for(int i = 1; i <= 9; i++) t.a[i] = i;
t.val = Cantor_expansion(t.a, 9);
while(gets(str))
{
int k = 0, idx;
for(int i = 0; str[i]; i++)
{
if(str[i] == 'x') a[++k] = 9, idx = k;
else if(str[i] >= '0' && str[i] <= '9') a[++k] = str[i] - '0';
}
if(! check(a, 9))
{
puts("unsolvable"); continue;
}
node p;
p.idx = idx;
memcpy(p.a, a, sizeof a);
p.val = Cantor_expansion(p.a, 9);
a_star(p, t);
int en = t.val;
k = 0;
while(pre[en] != -1) str[k++] = op[en], en = pre[en];
reverse(str, str + k);
str[k++] = '\0';
puts(str);
}
return 0;
}