题解——八数码难题
思路
由明确的两种状态可以想到
D(double)BFS即双向BFS
输入的是个
283104765
然而 凡是搜索都有个标记即vis[]
但 按题意3 * 3的地图不好标记(也许用map可以)
于是直接用一维dir数组改成
int dir[4] = {1,-3,-1,3};
但九位数也是开不了的
就有一个cantor展开式
状态压缩一下
inline int cantor(int a[])
{
int i,j,ans = 0;
for(i = 0;i < 9;i++)
{
int s = 0;
for(j = i + 1;j < 9;j++)
if(a[j] < a[i]) s++;
ans += s * fac[8 - i];
}
return ans;
}
然后就行了 DBFS直接套板子啊
优化
其实就上面的好像就可以过了
但还有个优化
与逆序对有关
将3 * 3 的图 弄成序列
可以得出结论:初始状态的逆序对模2必须等于目标状态的逆序对模2,否则无法达成,输出-1
证明
可以转为 初始状态无论如何改变其奇偶性不变
位置图
1 2 3
4 5 6
7 8 9
将3 * 3 的图 弄成序列
1 2 3 4 5 6 7 8 9
注意 0 不算入队列 所以上面实际上是8个
~~
2 8 3
1 0 4
7 6 5
可见 当位置5的0与位置8的6交换会有几种情况
位置n的数 为方便简写为(n)
Case1:(6)和(7)中有0个比(8)小
则逆序对+ 2
Case2:(6)和(7)中有1个比(8)小
则逆序对- 1 + 1
Case2:(6)和(7)中有2个比(8)小
则逆序对+ 2
综上逆序对奇偶性不变
其它交换情况可以类似讨论
代码
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int MAXN = 400000;
int step[2][MAXN],fac[10] = {1,1,2,6,24,120,720,5040,40320,362880};
bool vis[2][MAXN];
int dir[4] = {1,-3,-1,3};
//cantor状态压缩
inline int cantor(int a[])
{
int i,j,ans = 0;
for(i = 0;i < 9;i++)
{
int s = 0;
for(j = i + 1;j < 9;j++)
if(a[j] < a[i]) s++;
ans += s * fac[8 - i];
}
return ans;
}
struct Node
{
int word[9],loc,ca;
bool f;
void node(int Word[],int LOC,int CA,bool F)
{
for(int i = 0;i < 9;i++) word[i] = Word[i];
loc = LOC,ca = CA,f = F;
}
}Start,order;
inline int dbfs()
{
queue<Node> q;
Start.f = 1,order.f = 0;
q.push(Start);
q.push(order);
int i;
while(!q.empty())
{
Node tmp = q.front();
q.pop();
if(vis[!tmp.f][tmp.ca])
return (step[1][tmp.ca] + step[0][tmp.ca]);
int Step = step[tmp.f][tmp.ca];
for(i = 0;i < 4;i++)
{
int poss = tmp.loc + dir[i];
//由于dir数组改了 判断也得改
if(0 <= poss&&poss < 9&&(tmp.loc % 3 == poss % 3||tmp.loc / 3 == poss / 3))
{
swap(tmp.word[tmp.loc],tmp.word[poss]);
int nca = cantor(tmp.word);
if(!vis[tmp.f][nca])
{
Node g;
vis[tmp.f][nca] = 1;
step[tmp.f][nca] = Step + 1;
g.node(tmp.word,poss,nca,tmp.f);
q.push(g);
}
swap(tmp.word[tmp.loc],tmp.word[poss]);
}
}
}
return -1;
}
//优化 求逆序对
inline int cutdown(int a[])
{
int i,j,q = 0;
for(i = 0;i < 9;i++)
for(j = i + 1;j < 9;j++)
if(a[i] != 0&&a[j] != 0&&a[j] < a[i]) q++;
return q;
}
int main()
{
int i;
for(i = 0;i < 9;i++)
{
scanf("%d",&Start.word[i]);
if(!Start.word[i]) Start.loc = i;
}
for(i = 0;i < 9;i++)
{
scanf("%d",&order.word[i]);
if(!order.word[i]) order.loc = i;
}
//优化
if(cutdown(Start.word) % 2 != cutdown(order.word) % 2)
{
printf("-1");
return 0;
}
Start.ca = cantor(Start.word);
vis[1][Start.ca] = 1;
order.ca = cantor(order.word);
vis[0][order.ca] = 1;
printf("%d",dbfs());
return 0;
}