八数码(A*+路径输出+逆序数+康托展开式hash)

来源:互联网 发布:视频解析接口源码 编辑:程序博客网 时间:2024/06/05 17:22

九宫重排

问题描述

给定九宫格的初始状态要求在有限步的操作内使其转化为目标状态且所得到的解是代价最小解(即移动的步数最少)。


基本要求

输入九宫格的初始状态和目标状态

输出重排的过程即途径的状态

空格用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 5void 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;//空格在最左边不能-1if (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 -->123456789value = 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 5int 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 5bool 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 5Dig 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->18stepsDig 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();}}


阅读全文
0 0
原创粉丝点击