一. 八數碼問題
八數碼問題也稱爲九宮問題。在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");
}
}