http://acm.hdu.edu.cn/showproblem.php?pid=1043
题意:将八数码变成1 2 3 4 5 6 7 8 x
思路:就是普通的A*,IDA*, 用一个数组保存路径,vis数组保存移动方向, 还有开始要判断其实状态的逆序数和目标状态的逆序数是否一样。
求解八数码的时候IDA* 确实比 A* 要快,代码也短一点,可悲的是,这两个代码竟然都比一年前写的慢,难道我还退步了啊,算了 也懒得优化了。
IDA*代码(655ms):
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 400000;
const char prt[4] = {'r', 'd', 'u', 'l'}; //方向输出
const int Move[4][2] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; //移动方向
const int fsh[10][2] = {{2, 2}, {0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}}; //目标状态每个数的坐标,用于计算曼哈顿距离
char sta[105]; //保存路径
int abs(int a){
return a < 0 ? -a : a;
}
int Manhattan(int *num) //曼哈顿距离
{
int sum = 0;
for(int i = 0; i < 9; i++){
sum += abs(fsh[num[i]][0] - i/3) + abs(fsh[num[i]][1] - i%3);
}
return sum;
}
int isok(int *mapn) //求逆序数
{
int sum=0;
for(int i = 0; i < 9; i++)
{
int num = 0;
if(mapn[i] == 0)
continue;
for(int k = i+1; k < 9; k++)
{
if(mapn[i] > mapn[k] && mapn[k])
{
num++;
}
}
sum += num;
}
return sum;
}
bool IdaStar(int *mapn, int x, int y, int pre, int step, int upper) //IDA*搜索
{
int nx, ny;
//四个方向
for(int i = 0; i < 4; i++){
nx = x + Move[i][0];
ny = y + Move[i][1];
if(pre + i == 3 || nx < 0 || nx > 2 || ny < 0 || ny > 2) //回到前驱或者出界
continue;
mapn[3 * x + y] = mapn[3 * nx + ny]; //交换
mapn[3 * nx + ny] = 0;
int mht = Manhattan(mapn); //计算曼哈顿距离(估价函数,相当于最少还要多少步)
if(mht == 0){ //到达终点
sta[step] = prt[i];
sta[step+1] = '\0';
return true;
}
if(mht + step <= upper){ //剪枝
sta[step] = prt[i];
if(IdaStar(mapn, nx, ny, i, step+1, upper))
return true;
}
mapn[3 * nx + ny] = mapn[3 * x + y]; //交换回来
mapn[3 * x + y] = 0;
}
return false;
}
int main()
{
char str[10];
while(cin>>str[0]){
for(int i = 1; i < 9; i++){
cin>>str[i];
}
int mapn[10], x, y;
for(int i = 0; i < 9; i++){
if(str[i] == 'x'){
x = i / 3;
y = i % 3;
mapn[i] = 0;
}
else
mapn[i] = str[i] - '0';
}
if(isok(mapn) % 2 == 1)
cout<<"unsolvable"<<endl;
else{
int ans = Manhattan(mapn);
if(ans == 0){
cout<<endl;
}
else{
int top = 0;
while(++top){ //top表示搜索的上限
if(IdaStar(mapn, x, y, -1, 0, top)) //搜索成功
break;
}
cout<<sta<<endl;
}
}
}
return 0;
}
A*代码(1513ms):
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn = 400000;
const char prt[4] = {'r', 'l', 'd', 'u'}; //方向
const int Move[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; //移动
const int dis[12] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800}; //计算康拓值的数组
const int fsh[10][2] = {{2, 2}, {0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}, {2, 2}}; //目标状态每个数的坐标,用于计算曼哈顿距离
struct node{
int key; //用康拓值保存图
int x, y; // 0 的位置
int step, mht; //step表示走了多少步,mht 表示最少还要多少步
friend bool operator < (node a, node b){ //优先队列,b.step+b.mht 小的先出队
return a.step+a.mht > b.step+b.mht;
}
};
int pre[maxn]; //保存前驱
int vis[maxn]; //用于判重,顺便保存移动方向
priority_queue<node> que; //优先队列
inline int abs(int a)
{
return a > 0 ? a : -a;
}
int Cantor(int *arg, int len) //康拓展开
{
int resl = 1;
for(int i = 0; i < len; i++){
int counts = 0;
for(int k = i + 1; k < len; k++){
if(arg[i] > arg[k]){
counts++;
}
}
resl = resl + dis[len-i-1] * counts;
}
return resl;
}
void InvCantor(int *arg, int num, int len) // 逆康拓
{
num = num - 1;
bool vis[12] = {false};
for(int i = 0; i < len; i++){
int mid = 0;
int cnt = num / dis[len-i-1];
while(++mid){
if(!vis[mid]){
if(cnt == 0)
break;
cnt--;
}
}
arg[i] = mid - 1;
vis[mid] = true;
num = num % dis[len-i-1];
}
}
int Manhattan(int *num) //曼哈顿距离
{
int sum = 0;
for(int i = 0; i < 9; i++){
sum += abs(fsh[num[i]][0] - i/3) + abs(fsh[num[i]][1] - i%3);
}
return sum;
}
int isok(int *mapn) //求逆序数
{
int sum=0;
for(int i = 0; i < 9; i++)
{
int num = 0;
if(mapn[i] == 0)
continue;
for(int k = i+1; k < 9; k++)
{
if(mapn[i] > mapn[k] && mapn[k])
{
num++;
}
}
sum += num;
}
return sum;
}
int print(int rt) //输出路径
{
if(pre[rt] == -1){
return 0;
}
print(pre[rt]);
cout<<prt[vis[rt]];
}
int Astar(int *mapn, int x, int y) //A*搜索
{
node w, r;
w.step = 0;
w.x = x, w.y = y;
w.mht = Manhattan(mapn);
w.key = Cantor(mapn, 9);
vis[w.key] = 0;
while(!que.empty()) que.pop();
que.push(w);
while(!que.empty())
{
r = que.top(), que.pop();
InvCantor(mapn, r.key, 9);
for(int i = 0; i < 4; i++){
w.x = r.x + Move[i][0];
w.y = r.y + Move[i][1];
if(w.x < 0 || w.x > 2 || w.y < 0 || w.y > 2)
continue;
mapn[(r.x * 3 + r.y)] = mapn[(w.x * 3 + w.y)]; //移动 0
mapn[(w.x * 3 + w.y)] = 0;
w.key = Cantor(mapn, 9); //计算康拓值
w.mht = Manhattan(mapn); //计算曼哈顿距离
if(vis[w.key] == -1){
w.step = r.step + 1;
que.push(w);
vis[w.key] = i;
pre[w.key] = r.key;
if(w.mht == 0){
print(w.key);
cout<<endl;
return 0;
}
}
mapn[(w.x * 3 + w.y)] = mapn[(r.x * 3 + r.y)]; 移动回来
mapn[(r.x * 3 + r.y)] = 0;
}
}
return -1;
}
int main()
{
char str[10];
while(cin>>str[0]){
for(int i = 1; i < 9; i++){
cin>>str[i];
}
int mapn[10], x, y;
for(int i = 0; i < 9; i++){
if(str[i] == 'x'){
x = i / 3;
y = i % 3;
mapn[i] = 0;
}
else
mapn[i] = str[i] - '0';
}
if(isok(mapn) % 2 == 1)
cout<<"unsolvable"<<endl;
else{
int ans = Manhattan(mapn);
if(ans == 0){
cout<<endl;
}
memset(pre, -1, sizeof(pre));
memset(vis, -1, sizeof(vis));
Astar(mapn, x, y);
}
}
return 0;
}