一. 八数码问题
八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。
二. 问题分析
三. 各种思路的代码实现
自己看了两天把各种思路的代码敲了下,题目是:POJ - 1077 Eight(松松可以过的) HDU - 1043 Eight(多组输入,容易卡内存,打表也能过)
境界一:暴力BFS+STL
//境界一:BFS+STL
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<set>
#include<queue>
#include<stack>
#include<map>
using namespace std;
string g;
set<string>vis;
queue<string>que;
struct node
{
string pre;
char op;
};
map<string, node>road;
bool moveUp(string &g)
{
int posx;
for (int i = 0; i < 9; i++)
{
if (g[i] == 'x')
{
posx = i;
break;
}
}
if (posx <= 2) return false;
else
{
swap(g[posx], g[posx - 3]);
return true;
}
}
bool moveDown(string &g)
{
int posx;
for (int i = 0; i < 9; i++)
{
if (g[i] == 'x')
{
posx = i;
break;
}
}
if (posx >= 6) return false;
else
{
swap(g[posx], g[posx + 3]);
return true;
}
}
bool moveLeft(string &g)
{
int posx;
for (int i = 0; i < 9; i++)
{
if (g[i] == 'x')
{
posx = i;
break;
}
}
if (posx == 0 || posx==3 || posx==6) return false;
else
{
swap(g[posx], g[posx - 1]);
return true;
}
}
bool moveRight(string &g)
{
int posx;
for (int i = 0; i < 9; i++)
{
if (g[i] == 'x')
{
posx = i;
break;
}
}
if (posx == 2 || posx==5 ||posx==8 ) return false;
else
{
swap(g[posx], g[posx + 1]);
return true;
}
}
int bfs()
{
while (!que.empty()) que.pop();
vis.clear();
road.clear();
que.push(g);
vis.insert(g);
while (!que.empty())
{
string loc = que.front();
que.pop();
if (loc == "12345678x")
return true;
for (int i = 0; i < 4; i++)
{
string noc = loc;
if (i == 0)
{
if (moveUp(noc) && vis.count(noc)==0)
{
que.push(noc);
vis.insert(noc);
road[noc].pre = loc;
road[noc].op = 'u';
}
}
else if (i == 1)
{
if (moveDown(noc) && vis.count(noc) == 0)
{
que.push(noc);
vis.insert(noc);
road[noc].pre = loc;
road[noc].op = 'd';
}
}
else if (i == 2)
{
if (moveLeft(noc) && vis.count(noc) == 0)
{
que.push(noc);
vis.insert(noc);
road[noc].pre = loc;
road[noc].op = 'l';
}
}
else if (i == 3)
{
if (moveRight(noc) && vis.count(noc) == 0)
{
que.push(noc);
vis.insert(noc);
road[noc].pre = loc;
road[noc].op = 'r';
}
}
}
}
return -1;
}
int main()
{
char c;
for (int i = 0; i < 9; i++)
{
cin >> c;
g += c;
}
int ans=bfs();
if (ans == -1)
cout << "unsolvable" << endl;
else
{
string np = "12345678x";
stack<char>sop;
while (np != g)
{
sop.push(road[np].op);
np = road[np].pre;
}
while (!sop.empty())
{
cout << sop.top();
sop.pop();
}
cout << endl;
}
}
境界二:BFS+哈希(康托展开)
//境界二:BFS+康托展开
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
typedef int State[9];
const int maxn = 1000000;
State st[maxn], goal; //状态数组,用来实现队列
int dis[maxn]; //距离数组
int fa[maxn]; //记录前一个状态的编号
char curop[maxn]; //记录达到该状态的对应操作
bool vis[maxn]; //结点是否访问过
const int dirx[] = { -1, 1, 0, 0 };
const int diry[] = { 0, 0, -1, 1 };
const char cop[] = { 'u', 'd', 'l', 'r' };
const int f[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };
//康托展开
int cantor(State s)
{
int ans = 0;
for (int i = 0; i<9; i++)
{
int tmp = 0;
for (int j = i + 1; j<9; j++)
if (s[j] < s[i]) tmp++;
ans += tmp * f[9 - i - 1]; //f[]为阶乘
}
return ans; //返回该字符串是全排列中第几大,从0开始
}
int bfs()
{
memset(vis, false, sizeof(vis)); //初始化查找表
vis[cantor(st[1])] = true;
int front = 1, rear = 2;
while (front < rear)
{
State& s = st[front]; //用引用简化代码
int cs = cantor(s);
if (memcmp(goal, s, sizeof(s)) == 0) return front; //找到目标状态,返回
int pos;
for (pos = 0; pos < 9; pos++) if (!s[pos]) break;
int x = pos / 3, y = pos % 3;
for (int d = 0; d < 4; d++)
{
int newx = x + dirx[d];
int newy = y + diry[d];
int newpos = newx * 3 + newy;
if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3)
{
State& t = st[rear];
memcpy(&t, &s, sizeof(s));
t[newpos] = 0;
t[pos] = s[newpos];
dis[rear] = dis[front] + 1;
int ct = cantor(t);
if (!vis[ct]) //结点合法且未访问,加入查找表,修改队尾指针
{
rear++;
curop[ct] = cop[d];
fa[ct] = cs;
vis[ct] = true;
}
}
}
front++; //扩展完毕后修改队首指针
}
return 0;
}
int main()
{
char c;
for (int i = 0; i < 9; i++)
{
cin >> c;
if (c == 'x') st[1][i] = 0;
else st[1][i] = c - '0';
}
goal[8] = 0;
for (int i = 0; i < 8; i++)
goal[i] = i + 1;
int ans = bfs();
if (!ans) printf("unsolvable\n");
else
{
stack<char>ansop;
int np = cantor(goal);
int sp = cantor(st[1]);
while (fa[np] != sp)
{
ansop.push(curop[np]);
np = fa[np];
}
ansop.push(curop[np]);
while (!ansop.empty())
{
printf("%c", ansop.top());
ansop.pop();
}
printf("\n");
}
}
境界三:逆向BFS+康托展开+打表
//境界3:逆向广搜+康托展开+打表
//答案和样例的不同,但没关系,八数码问题是有多个解的,有Special Judge
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<string>
using namespace std;
const int maxn = 363000;
bool vis[maxn];
string path[maxn]; //方便的存下路径
const int dirx[] = { -1, 1, 0, 0 };
const int diry[] = { 0, 0, -1, 1 };
const char cop[] = { 'd', 'u', 'r', 'l' }; //和上面的方向数组相反,因为是逆向bfs
const int f[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };
struct node
{
int map[9];
int pos;
}que[maxn]; //用数组实现栈,stl代价太大
//直接求逆序数,因为移动x并不会改变map的逆序数的奇偶性,很容易想到
//而原始逆序数为0,故无解的情况是:逆序数%2!=0
int getnixu(int s[])
{
int ret = 0;
for (int i = 0; i < 9; i++)
{
if (s[i] == 0) continue;
for (int j = 0; j < i; j++)
{
if (s[j] == 0) continue;
if (s[i] < s[j]) ret++;
}
}
return ret;
}
//康托展开
int cantor(int s[])
{
int ans = 0;
for (int i = 0; i<9; i++)
{
int tmp = 0;
for (int j = i + 1; j<9; j++)
if (s[j] < s[i]) tmp++;
ans += tmp * f[9 - i - 1]; //f[]为阶乘
}
return ans; //返回该字符串是全排列中第几大,从0开始
}
void bfs()
{
memset(vis, false, sizeof(vis));
int tag[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 0 }; //设置最终状态
int ct_tag = cantor(tag);
vis[ct_tag] = true;
path[ct_tag] = "";
memcpy(que[1].map , tag, sizeof(tag));
que[1].pos = 8;
int front = 1, rear = 2;
while (front < rear)
{
node loc = que[front];
int ct_loc = cantor(loc.map);
int x = loc.pos / 3, y = loc.pos % 3;
for (int d = 0; d < 4; d++)
{
int newx = x + dirx[d];
int newy = y + diry[d];
int newpos = newx * 3 + newy;
if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3)
{
node noc;
memcpy(noc.map, loc.map, sizeof(noc.map));
noc.map[newpos] = 0;
noc.map[loc.pos] = loc.map[newpos];
noc.pos = newpos;
int ct_noc = cantor(noc.map);
if (!vis[ct_noc])
{
que[rear++]=noc;
vis[ct_noc] = true;
path[ct_noc] = cop[d] + path[ct_loc];
}
}
}
front++;
}
}
int main()
{
bfs();
char c;
while (cin >> c)
{
int g[15];
if (c == 'x') g[0] = 0;
else g[0] = c - '0';
for (int i = 1; i < 9; i++)
{
cin >> c;
if (c == 'x') g[i] = 0;
else g[i] = c - '0';
}
int temp = cantor(g);
if (getnixu(g) % 2)
cout << "unsolvable" << endl;
else
cout << path[temp] << endl;
}
}
境界四:双向广搜+康托展开
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<string>
using namespace std;
const int maxn = 363000;
int vis[maxn];
string path[maxn];
const int dirx[] = { -1, 1, 0, 0 };
const int diry[] = { 0, 0, -1, 1 };
const char zop[] = { 'u', 'd', 'l', 'r' };
const char cop[] = { 'd', 'u', 'r', 'l' };
const int f[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };
int tag[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 0 };
int start[9];
int spos;
struct node
{
int map[9];
int pos;
}que[maxn];
int getnixu(int s[])
{
int ret = 0;
for (int i = 0; i < 9; i++)
{
if (s[i] == 0) continue;
for (int j = 0; j < i; j++)
{
if (s[j] == 0) continue;
if (s[i] < s[j]) ret++;
}
}
return ret;
}
int cantor(int s[])
{
int ans = 0;
for (int i = 0; i<9; i++)
{
int tmp = 0;
for (int j = i + 1; j<9; j++)
if (s[j] < s[i]) tmp++;
ans += tmp * f[9 - i - 1];
}
return ans;
}
string bfs_d()
{
memset(vis, 0, sizeof(vis));
memcpy(que[1].map, tag, sizeof(tag));
que[1].pos = 8;
int ct_tag = cantor(tag);
vis[ct_tag] = 2;
path[ct_tag] = "";
memcpy(que[2].map, start, sizeof(start));
que[2].pos = spos;
int ct_start = cantor(start);
vis[ct_start] = 1;
path[ct_start] = "";
int front = 1, rear = 3;
while (front < rear)
{
node loc = que[front];
int ct_loc = cantor(loc.map);
int x = loc.pos / 3, y = loc.pos % 3;
for (int d = 0; d < 4; d++)
{
int newx = x + dirx[d];
int newy = y + diry[d];
int newpos = newx * 3 + newy;
if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3)
{
node noc;
memcpy(noc.map, loc.map, sizeof(noc.map));
noc.map[newpos] = 0;
noc.map[loc.pos] = loc.map[newpos];
noc.pos = newpos;
int ct_noc = cantor(noc.map);
if (!vis[ct_noc])
{
que[rear++] = noc;
vis[ct_noc] = vis[ct_loc];
if(vis[ct_noc]==2) path[ct_noc] = cop[d] + path[ct_loc];
else path[ct_noc] = path[ct_loc] + zop[d];
}
else if (vis[ct_noc]!=vis[ct_loc])
{
if (vis[ct_noc] == 1) return path[ct_noc] +cop[d]+ path[ct_loc];
else return path[ct_loc] +zop[d]+ path[ct_noc];
}
}
}
front++;
}
return "unsolvable";
}
int main()
{
char c;
for (int i = 0; i < 9; i++)
{
cin >> c;
if (c == 'x')
{
start[i] = 0; spos = i;
}
else start[i] = c - '0';
}
if (getnixu(start) % 2)
cout << "unsolvable" << endl;
else
cout << bfs_d() << endl;
}
打表,HDU 3567-Eight Ⅱ
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<string>
using namespace std;
const int maxn = 363000;
bool vis[maxn];
int pre[9][maxn];
char preop[9][maxn];
int dis[9][maxn];
const int dirx[] = { 1, 0, 0, -1 };
const int diry[] = { 0, -1, 1, 0 };
const char cop[] = "dlru";
const int f[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };
int tag[9][9];
void getTag()
{
for (int px = 0; px < 9; px++)
{
int j = 1;
for (int i = 0; i < 9; i++)
{
if (i == px) tag[px][i] = 0;
else tag[px][i] = j++;
}
}
}
struct node
{
int map[9];
int pos;
}que[maxn];
//康托展开
int cantor(int s[])
{
int ans = 0;
for (int i = 0; i<9; i++)
{
int tmp = 0;
for (int j = i + 1; j<9; j++)
if (s[j] < s[i]) tmp++;
ans += tmp * f[9 - i - 1];
}
return ans;
}
void bfs(int px)
{
memset(vis, false, sizeof(vis));
int ct_tag = cantor(tag[px]);
vis[ct_tag] = true;
dis[px][ct_tag] = 0;
pre[px][ct_tag] = -1;
memcpy(que[1].map, tag[px], sizeof(tag[px]));
que[1].pos = px;
int front = 1, rear = 2;
while (front < rear)
{
node loc = que[front];
int ct_loc = cantor(loc.map);
int x = loc.pos / 3, y = loc.pos % 3;
for (int d = 0; d < 4; d++)
{
int newx = x + dirx[d];
int newy = y + diry[d];
int newpos = newx * 3 + newy;
if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3)
{
node noc;
memcpy(noc.map, loc.map, sizeof(noc.map));
noc.map[newpos] = 0;
noc.map[loc.pos] = loc.map[newpos];
noc.pos = newpos;
int ct_noc = cantor(noc.map);
if (!vis[ct_noc])
{
que[rear++] = noc;
vis[ct_noc] = true;
preop[px][ct_noc] = cop[d];
pre[px][ct_noc] = ct_loc;
dis[px][ct_noc] = dis[px][ct_loc] + 1;
}
}
}
front++;
}
}
void output(int p, int np)
{
if (pre[p][np] == -1) return;
output(p, pre[p][np]);
printf("%c", preop[p][np]);
}
int main()
{
getTag();
for (int i = 0; i < 9; i++)
bfs(i);
int casen;
cin >> casen;
for (int cas = 1; cas <= casen; cas++)
{
string s1, s2;
cin >> s1 >> s2;
int p;
int g1[9], g2[9];
int ji = 1;
for (int i = 0; i < 9; i++)
{
if (s1[i] == 'X') { p = i; g1[0] = 0; }
else g1[s1[i]-'0'] = ji++;
}
for (int i = 0; i < 9; i++)
{
if (s2[i] == 'X') g2[i] = g1[0];
else g2[i] = g1[s2[i] - '0'];
}
int temp = cantor(g2);
printf("Case %d: %d\n", cas, dis[p][temp]);
output(p, temp);
printf("\n");
}
}