九宫重排
问题描述
给定九宫格的初始状态,要求在有限步的操作内,使其转化为目标状态,且所得到的解是代价最小解(即移动的步数最少)。如:
基本要求
输入:九宫格的初始状态和目标状态
输出:重排的过程,即途径的状态
空格用0来表示
BFS 做法,状态数为9!,可以用set存,就是效率很慢
#include<cstdio>
#include<queue>
#include<algorithm>
#include<iostream>
#include<set>
using namespace std;
struct Dig
{
int p[9];//当前的状态
int len;//步数
int pos;//空格所在的位置
int ans[32][9];//存取路径 因为最长的路径不可能大于2的32次方
};
set<int>s;
int dir[4] = { 1,-1,3,-3 };
Dig start;
//2 8 3
//1 0 4
//7 6 5
void bfs()
{
queue<Dig>q;
q.push(start);
while (!q.empty())
{
Dig tmp1 = q.front();
if (tmp1.p[0] == 1 && tmp1.p[1] == 2 && tmp1.p[2] == 3 && tmp1.p[3] == 8 && tmp1.p[4] == 0 && tmp1.p[5] == 4 && tmp1.p[6] == 7 && tmp1.p[7] == 6 && tmp1.p[8] == 5)
{
printf("You must move it at least %d times\n", tmp1.len);
for (int i = 0; i <= tmp1.len; i++)
{
printf("The %d times move:\n", i);
for (int j = 0; j < 9; j++)
{
if (j % 3 == 0)puts("");
printf("%5d", tmp1.ans[i][j]);
}
puts("");
}
return;
}
tmp1.len++;
q.pop();
for (int i = 0; i < 4; i++)
{
Dig tmp2 = tmp1;
tmp2.pos = tmp2.pos + dir[i];
if (dir[i] == 1 && (tmp2.pos == 3 || tmp2.pos == 6))continue;//空格在最左边不能-1
if (dir[i] == -1 && (tmp2.pos == 2 || tmp2.pos == 5))continue;//同理
if (tmp2.pos >= 0 && tmp2.pos < 9)//必须在范围内
{
swap(tmp2.p[tmp2.pos], tmp2.p[tmp2.pos - dir[i]]);//移动
int status = 0;
for (int i = 0; i < 9; i++)
status = status * 10 + tmp2.p[i] + 1;
if (s.count(status)==0) { //没有重复出现
for (int i = 0; i < 9; i++)
tmp2.ans[tmp2.len][i] = tmp2.p[i];
s.insert(status);
q.push(tmp2); }
}
}
}
puts("找不到解");
}
int main()
{
while (1)
{
puts("Input the inital status:");
for (int i = 0; i < 9; i++)
{
scanf("%d", &start.p[i]);
if (start.p[i] == 0)start.pos = i;
start.ans[0][i] = start.p[i];
}
start.len = 0;
s.clear();
int value=0;
for (int i = 0; i < 9; i++)//0 1 2 3 4 5 6 7 8 -->123456789
value = value * 10 + (start.p[i] + 1);
s.insert(value);
bfs();
}
}
BFS+康托hash判重就快了很多了
#include<cstdio>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
struct Dig
{
int p[9];
int len;
int pos;
int ans[32][9];
};
int fac[] = { 1,1,2,6,24,120,720,5040,40320 };
bool vis[555555];
int dir[4] = { 1,-1,3,-3 };
Dig start;
//2 8 3
//1 0 4
//7 6 5
int KT(Dig s)//康托展开式hash
{
int i, j, t, sum;
sum = 0;
for (i = 0; i<9; i++)
{
t = 0;
for (j = i + 1; j<9; j++)
if (s.p[j] < s.p[i])
t++;
sum += t*fac[9 - i - 1];
}
return sum + 1;
}
void bfs()
{
queue<Dig>q;
q.push(start);
while (!q.empty())
{
Dig tmp1 = q.front();
if (tmp1.p[0] == 1 && tmp1.p[1] == 2 && tmp1.p[2] == 3 && tmp1.p[3] == 8 && tmp1.p[4] == 0 && tmp1.p[5] == 4 && tmp1.p[6] == 7 && tmp1.p[7] == 6 && tmp1.p[8] == 5)
{
printf("You must move it at least %d times\n", tmp1.len);
for (int i = 0; i <= tmp1.len; i++)
{
printf("The %d times move:\n", i);
for (int j = 0; j < 9; j++)
{
if (j % 3 == 0)puts("");
printf("%5d", tmp1.ans[i][j]);
}
puts("");
}
return;
}
tmp1.len++;
q.pop();
for (int i = 0; i < 4; i++)
{
Dig tmp2 = tmp1;
tmp2.pos = tmp2.pos + dir[i];
if (dir[i] == 1 && (tmp2.pos == 3 || tmp2.pos == 6))continue;
if (dir[i] == -1 && (tmp2.pos == 2 || tmp2.pos == 5))continue;
if (tmp2.pos >= 0 && tmp2.pos < 9)
{
swap(tmp2.p[tmp2.pos], tmp2.p[tmp2.pos - dir[i]]);
int status = KT(tmp2);
if (!vis[status]) {
for (int i = 0; i < 9; i++)
tmp2.ans[tmp2.len][i] = tmp2.p[i];
vis[status] = true;
q.push(tmp2); }
}
}
}
puts("找不到解");
}
int main()
{
while (1)
{
puts("Input the inital status:");
for (int i = 0; i < 9; i++)
{
scanf("%d", &start.p[i]);
if (start.p[i] == 0)start.pos = i;
start.ans[0][i] = start.p[i];
}
start.len = 0;
memset(vis, false, sizeof(vis));
vis[KT(start)] = true;
bfs();
}
}
用A*的话,如果八数码无解还是和BFS的效率一样,所以加个逆序数判断有无解
这个A*的函数是 f(n)=g(n)+h(n)
g(n)代表步数
h(n)代表当前八数码不在位的个数
剪枝原理:
逆序数:对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序。逆序对的总数称为逆序数
只要终止状态和起始状态的逆序数(空的位置不算)奇偶性不同,就一定不能达到目标状态。
分析:向左或者向右移动,逆序数的奇偶行不变....0,xt,xt+1...,将.0和xt交换,奇偶性是不变的
对 x1 x2 x3
x4 x5 x6
x7 0 x8
将0和x5交换,x1 x2 x3 x4 x5 x6 x7 0 x8,下面分三种情况
a)若 x5>x6 && x5> x7,则逆序数+2
a)若 x5 <x6 && x5<x7,则逆序数-2
a)若 x5 在6 和x7之间,则逆序数不变
通过以上分析可知:只有起始状态可终止状态逆序数奇偶性相同才能转换
#include<cstdio>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
struct Dig
{
int p[9];
int len;
int pos;
int ans[32][9];
int cost;
bool operator < (const Dig &a) const {
return cost>a.cost;//最小值优先
}
};
int fac[] = { 1,1,2,6,24,120,720,5040,40320 };
bool vis[555555];
int dir[4] = { 1,-1,3,-3 };
Dig start;
//2 8 3
//1 0 4
//7 6 5
bool inverse(Dig s)
{
int t = 0, x, y;
for (int i = 1; i<9; i++)
for (int j = 0; j<i; j++)
{
if (s.p[j] == 0)continue;
else x = s.p[j];
if (s.p[i] == 0)continue;
else y = s.p[i];
if (x>y)
t++;
}
if (t & 1)
return true;
return false;
}
int KT(Dig s)//康托展开式hash
{
int i, j, t, sum;
sum = 0;
for (i = 0; i<9; i++)
{
t = 0;
for (j = i + 1; j<9; j++)
if (s.p[j] < s.p[i])
t++;
sum += t*fac[9 - i - 1];
}
return sum + 1;
}
int fcost(Dig tmp1)
{
int cnt = 9;
if (tmp1.p[0] == 1)cnt--;
if (tmp1.p[1] == 2)cnt--;
if (tmp1.p[2] == 3)cnt--;
if (tmp1.p[3] == 8)cnt--;
if (tmp1.p[4] == 0)cnt--;
if (tmp1.p[5] == 4)cnt--;
if (tmp1.p[6] == 7)cnt--;
if (tmp1.p[7] == 6)cnt--;
if (tmp1.p[8] == 5)cnt--;
return cnt;
}
void Astar()
{
priority_queue<Dig>q;
q.push(start);
while (!q.empty())
{
Dig tmp1 = q.top();
if (tmp1.p[0] == 1 && tmp1.p[1] == 2 && tmp1.p[2] == 3 && tmp1.p[3] == 8 && tmp1.p[4] == 0 && tmp1.p[5] == 4 && tmp1.p[6] == 7 && tmp1.p[7] == 6 && tmp1.p[8] == 5)
{
printf("You must move it at least %d times\n", tmp1.len);
for (int i = 0; i <= tmp1.len; i++)
{
printf("The %d times move:\n", i);
for (int j = 0; j < 9; j++)
{
if (j % 3 == 0)puts("");
printf("%5d", tmp1.ans[i][j]);
}
puts("");
}
return;
}
tmp1.len++;
q.pop();
for (int i = 0; i < 4; i++)
{
Dig tmp2 = tmp1;
tmp2.pos = tmp2.pos + dir[i];
if (dir[i] == 1 && (tmp2.pos == 3 || tmp2.pos == 6))continue;
if (dir[i] == -1 && (tmp2.pos == 2 || tmp2.pos == 5))continue;
if (tmp2.pos >= 0 && tmp2.pos < 9)
{
swap(tmp2.p[tmp2.pos], tmp2.p[tmp2.pos - dir[i]]);
int status = KT(tmp2);
if (!vis[status]) {
for (int i = 0; i < 9; i++)
tmp2.ans[tmp2.len][i] = tmp2.p[i];
tmp2.cost = tmp2.len + fcost(tmp2);
vis[status] = true;
q.push(tmp2); }
}
}
}
}
int main()
{
while (1)
{
puts("Input the inital status:");
for (int i = 0; i < 9; i++)
{
scanf("%d", &start.p[i]);
if (start.p[i] == 0)start.pos = i;
start.ans[0][i] = start.p[i];
}
if (!inverse(start))
{
printf("unsolvable\n");
continue;
}
start.len = 0;
start.cost = start.len + fcost(start);
memset(vis, false, sizeof(vis));
vis[KT(start)] = true;
Astar();
}
}
输入目标状态
#include<cstdio>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
struct Dig
{
int p[9];
int len;
int pos;
int ans[32][9];
int cost;
bool operator < (const Dig &a) const {
return cost>a.cost;//最小值优先
}
};
int fac[] = { 1,1,2,6,24,120,720,5040,40320 };
bool vis[555555];
int dir[4] = { 1,-1,3,-3 };
Dig start;
//2 8 3
//1 0 4
//7 6 5
Dig last;
bool inverse(Dig s)
{
int t = 0, x, y;
for (int i = 1; i<9; i++)
for (int j = 0; j<i; j++)
{
if (s.p[j] == 0)continue;
else x = s.p[j];
if (s.p[i] == 0)continue;
else y = s.p[i];
if (x>y)
t++;
}
if (t & 1)return true;
return false;
}
void input()
{
puts("Input the inital status:");
for (int i = 0; i < 9; i++)
{
scanf("%d", &start.p[i]);
if (start.p[i] == 0)start.pos = i;
start.ans[0][i] = start.p[i];
}
puts("Input the last status:");
for (int i = 0; i < 9; i++)
{
scanf("%d",&last.p[i]);
}
}
int KT(Dig s)//康托展开式hash
{
int i, j, t, sum;
sum = 0;
for (i = 0; i<9; i++)
{
t = 0;
for (j = i + 1; j<9; j++)
if (s.p[j] < s.p[i])
t++;
sum += t*fac[9 - i - 1];
}
return sum + 1;
}
int fcost(Dig s)
{
int cnt = 0;
for (int i = 0; i <9 ; i++)
{
if (s.p[i] != last.p[i])cnt++;
}
return cnt;
}
void Astar()
{
priority_queue<Dig>q;
bool OK ;
q.push(start);
while (!q.empty())
{
Dig tmp1 = q.top();
OK = true;
for (int i = 0; i < 9; i++)
{
if (tmp1.p[i] != last.p[i]) { OK = false; break; }
}
if (OK)
{
printf("You must move it at least %d times\n", tmp1.len);
for (int i = 0; i <= tmp1.len; i++)
{
printf("The %d times move:\n", i);
for (int j = 0; j < 9; j++)
{
if (j % 3 == 0)puts("");
printf("%5d", tmp1.ans[i][j]);
}
puts("");
}
return;
}
tmp1.len++;
q.pop();
for (int i = 0; i < 4; i++)
{
Dig tmp2 = tmp1;
tmp2.pos = tmp2.pos + dir[i];
if (dir[i] == 1 && (tmp2.pos == 3 || tmp2.pos == 6))continue;
if (dir[i] == -1 && (tmp2.pos == 2 || tmp2.pos == 5))continue;
if (tmp2.pos >= 0 && tmp2.pos < 9)
{
swap(tmp2.p[tmp2.pos], tmp2.p[tmp2.pos - dir[i]]);
int status = KT(tmp2);
if (!vis[status]) {
for (int i = 0; i < 9; i++)
tmp2.ans[tmp2.len][i] = tmp2.p[i];
tmp2.cost = tmp2.len + fcost(tmp2);
vis[status] = true;
q.push(tmp2); }
}
}
}
}
int main()
{
while (1)
{
input();
bool invs = inverse(start);
bool invl= inverse(last);
if (invs!=invl)
{
printf("unsolvable\n");
continue;
}
start.len = 0;
start.cost = start.len + fcost(start);
memset(vis, false, sizeof(vis));
vis[KT(start)] = true;
Astar();
}
}
A* +曼哈顿距离
#include<cstdio>
#include<queue>
#include<algorithm>
#include<iostream>
using namespace std;
struct Dig
{
int p[9];
int len;
int pos;
int ans[32][9];
int cost;
bool operator < (const Dig &a) const {
return cost>a.cost;//最小值优先
}
};
int dir[4] = { 1,-1,3,-3 };
int fac[] = { 1,1,2,6,24,120,720,5040,40320 };
bool vis[555555];
bool first;
int lpos[9];
Dig start;
//2 8 3 1 0 4 7 6 5
//1 2 3 8 0 4 7 6 5
//2 1 6 4 0 8 7 5 3->18steps
Dig last;
bool inverse(Dig s)
{
int t = 0, x, y;
for (int i = 1; i<9; i++)
for (int j = 0; j<i; j++)
{
if (s.p[j] == 0)continue;
else x = s.p[j];
if (s.p[i] == 0)continue;
else y = s.p[i];
if (x>y)
t++;
}
if (t & 1)return true;
return false;
}
void input()
{
puts("Input the inital status:");
for (int i = 0; i < 9; i++)
{
scanf("%d", &start.p[i]);
if (start.p[i] == 0)start.pos = i;
start.ans[0][i] = start.p[i];
}
puts("Input the last status:");
for (int i = 0; i < 9; i++)
{
scanf("%d",&last.p[i]);
}
}
int KT(Dig s)//康托展开式hash
{
int i, j, t, sum;
sum = 0;
for (i = 0; i<9; i++)
{
t = 0;
for (j = i + 1; j<9; j++)
if (s.p[j] < s.p[i])
t++;
sum += t*fac[9 - i - 1];
}
return sum + 1;
}
int fcost(Dig s)//manhattan距离
{
int mlen = 0,x,y;
if (first)
{
for (int i = 0; i < 9; i++)
{
lpos[last.p[i]] = i;
}
first = false;
}
for (int i = 0; i < 9; i++)
{
x = i % 3;
y = i / 3;
mlen += abs(lpos[s.p[i]]%3-x)+abs(lpos[s.p[i]]/3-y);
}
return mlen;
}
void Astar()
{
priority_queue<Dig>q;
bool OK ;
q.push(start);
while (!q.empty())
{
Dig tmp1 = q.top();
OK = true;
for (int i = 0; i < 9; i++)
{
if (tmp1.p[i] != last.p[i]) { OK = false; break; }
}
if (OK)
{
printf("You must move it at least %d times\n", tmp1.len);
for (int i = 0; i <= tmp1.len; i++)
{
printf("The %d times move:\n", i);
for (int j = 0; j < 9; j++)
{
if (j % 3 == 0)puts("");
printf("%5d", tmp1.ans[i][j]);
}
puts("");
}
return;
}
tmp1.len++;
q.pop();
for (int i = 0; i < 4; i++)
{
Dig tmp2 = tmp1;
tmp2.pos = tmp2.pos + dir[i];
if (dir[i] == 1 && (tmp2.pos == 3 || tmp2.pos == 6))continue;
if (dir[i] == -1 && (tmp2.pos == 2 || tmp2.pos == 5))continue;
if (tmp2.pos >= 0 && tmp2.pos < 9)
{
swap(tmp2.p[tmp2.pos], tmp2.p[tmp2.pos - dir[i]]);
int status = KT(tmp2);
if (!vis[status]) {
for (int i = 0; i < 9; i++)
tmp2.ans[tmp2.len][i] = tmp2.p[i];
tmp2.cost = tmp2.len + fcost(tmp2);
vis[status] = true;
q.push(tmp2); }
}
}
}
}
int main()
{
while (1)
{
input();
bool invs = inverse(start);
bool invl= inverse(last);
if (invs!=invl)
{
printf("unsolvable\n");
continue;
}
first = true;
start.len = 0;
start.cost = start.len + fcost(start);
memset(vis, false, sizeof(vis));
vis[KT(start)] = true;
Astar();
}
}