八数码(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
- 八数码(A*+路径输出+逆序数+康托展开式hash)
- hdoj--1043--八数码--bfs||A*(HASH判重--康托)
- 八数码第七境界——A*之曼哈顿+康托展开判重+回溯记录路径+逆序数判无解
- 八数码问题,bfs,hash,康托
- HDU 1043 八数码问题 A*搜索+康拓展开+逆序对判断+路径输出
- 借助八数码问题,双向广搜,康托展开,逆序数奇偶性
- HDU 1043 双向广搜 八数码 康托展开 逆序数
- 八数码第六境界——双向BFS+康托展开判重+回溯记录路径+逆序数判无解
- 八数码(康托展开)
- 康托展开(八数码问题)
- codevs1225 八数码难题(A*搜索+康托展开)
- POJ1077 HDU1043 Eight 八数码 (A*+康托展开)
- POJ1077&HDU1043 Eight 八数码第八境界 IDA* hash 康托展开 奇偶剪枝
- hdu1043Eight (经典的八数码)(康托展开+BFS)
- hdu1043Eight (经典的八数码)(康托展开+BFS)
- BFS+康托展开(洛谷1379 八数码难题)
- POJ 1077 八数码(康托展开+暴力bfs)
- 康托展开(逆序数)
- [BZOJ3110][Zjoi2013]-K大数查询-树套树
- 真正的Cortex-A9四核_迅为4418开发板_畅玩4418开发板
- 【React】React+Redux+Ajax 点餐项目 完整流程【二】
- 第一个爬虫-知乎用户关系之登录
- Error:Execution failed for task ':app:processDebugManifest'.错误解决
- 八数码(A*+路径输出+逆序数+康托展开式hash)
- SeeBar自定义样式需要注意的地方,否则会出现闪烁的效果
- linux中java -version与etc/profile配置的java版本不一样解决办法
- 联想努力推动其数据中心业务的发展
- oracle中if/else功能的实现的3种写法
- js获取及判断键盘按键的方法
- LearningIOS
- 周末大作业增删改
- mysql 删除重复数据