题目描述:
http://acm.hdu.edu.cn/showproblem.php?pid=1043
中文大意:
经典八数码问题。
给定初始状态,要求变换到目标状态并输出移动过程。
目标状态固定为:1 2 3 4 5 6 7 8 x 。
思路:
采用逆向 BFS + 康托展开判重 + 打表的方法来做这道题。
八数码问题一共有 9! 种状态,每种状态对应一个 cantor 值。
以目标状态为 BFS 起始状态,搜索这 9! 种状态。
移动信息记录在 paths[362880] 数组中。
paths[i] 中的 i :现状态的 cantor 值,
pahts[i].from :前状态的 cantor 值,
paths[i].dir :前状态->现状态的移动方向。
因为是逆向 BFS,所以输出的时候需要反过来,即上对下,左对右。
队列节点记录的是当前八数码状态和 “x” 的位置。
在弹出队列首节点,确定了当前八数码的状态信息后,下一步有 4 种选择:“x” 上下左右移动。
“康托展开”用来计算当前状态的 cantor 值,判断该状态是否已经被访问 visited[cantor]。
注意:①移动信息不能直接记录在 string 中,否则会超内存限制;
②杭电oj 上的这道题有多组数据,需要 while(cin>>temp)。
代码:
#include<bits/stdc++.h> using namespace std; struct node{ int state[9];//八数码状态 int pos;//‘x‘位置 int g,h;//g:bfs层数,即初始状态到现状态的代价 //h:各点->目标位置的曼哈顿距离之和 //从小到大排序 bool operator<(const node& x)const{ return (g + h) > (x.g + x.h); } }; int start[9]; int goal[9] = {1,2,3,4,5,6,7,8,0}; //判断开始状态是否有解 //若逆序数为偶数,则有解 //不统计‘x‘ bool have_result(){ int sum = 0; for(int i=0;i<9;i++){ if(!start[i]){ continue; } for(int j=i+1;j<9;j++){ if(start[j] && start[i] > start[j]){ sum++; } } } if(sum % 2 == 0){ return true; } return false; } //移动信息 struct node2{ int from;//前状态的 cantor 值 int dir;//前状态->现状态的移动方向 }paths[362880]; //Cantor 计算会用到的常数 int factory[] = {1,1,2,6,24,120,720,5040,40320,362880}; int Cantor(int state[], int n){ int result = 0; for(int i=0;i<n;i++){ int counted = 0; for(int j=i+1;j<n;j++){ if(state[i] > state[j]){ counted++; } } result += counted * factory[n-i-1]; } return result; } int gpos[9][2] = {{2,2},{0,0},{1,0},{2,0},{0,1},{1,1},{2,1},{0,2},{1,2}}; //计算曼哈顿距离 int mht_dis(int state[]){ int result = 0; for(int i=0;i<9;i++){ if(state[i]){ int x = i % 3; int y = i / 3; result += abs(x - gpos[state[i]][0]) + abs(y - gpos[state[i]][1]); } } return result; } char dir_str[4] = {‘u‘,‘d‘,‘l‘,‘r‘}; void print(int cantor){ char result[362880]; int len = 0; //倒序遍历 while(paths[cantor].from != -1){ result[len] = dir_str[paths[cantor].dir]; len++; cantor = paths[cantor].from; } for(int i=len-1;i>=0;i--){ printf("%c", result[i]); } printf("\n"); } bool visited[362880] = {false}; int dir[4][2] = {{0,-1},{0,1},{-1,0},{1,0}}; void a_star(){ node head,next; memcpy(head.state, start, sizeof(start)); for(int i=0;i<9;i++){ if(head.state[i] == 0){ head.pos = i; break; } } head.g = 0; head.h = mht_dis(head.state); int h_cantor = Cantor(head.state, 9); memset(visited, false, sizeof(visited)); visited[h_cantor] = true; paths[h_cantor].from = -1; priority_queue<node> q; q.push(head); int g_cantor = Cantor(goal, 9); while(!q.empty()){ head = q.top(); q.pop(); h_cantor = Cantor(head.state, 9); if(h_cantor == g_cantor){ print(g_cantor); return; } int px = head.pos % 3; int py = head.pos / 3; for(int i=0;i<4;i++){ int nx = px + dir[i][0]; int ny = py + dir[i][1]; if(nx >= 0 && nx < 3 && ny >= 0 && ny < 3){ memcpy(next.state, head.state, sizeof(head.state)); next.pos = ny * 3 + nx; swap(next.state[next.pos], next.state[head.pos]); int n_cantor = Cantor(next.state, 9); if(!visited[n_cantor]){ visited[n_cantor] = true; next.g = head.g + 1; next.h = mht_dis(next.state); paths[n_cantor].from = h_cantor; paths[n_cantor].dir = i; q.push(next); } } } } } int main(){ char temp; while(cin>>temp){ start[0] = temp - ‘0‘; if(start[0] > 9){ start[0] = 0; } for(int i=1;i<9;i++){ cin>>temp; start[i] = temp - ‘0‘; if(start[i] > 9){ start[i] = 0; } } if(!have_result()){ cout << "unsolvable" << endl; } else{ a_star(); } } }
【A*算法 + 康托展开】hdu 1043 Eight(八数码问题)
原文:https://www.cnblogs.com/bjxqmy/p/14387149.html