一道十分经典的搜索问题,也顺便考察了一下八数码有解无解判断的知识和哈希的方法,所以导致了有很多种解法。不过杭电数据比北大的强,所以花了我好大一番功夫。我在这里采用了A*+康托展开(哈希)的方法,效果还不错。
A*算法主要是估值函数函数,我这里的启发式函数采用了当前状态的每个数字距目标状态每个数字的曼哈顿距离之和。
至于用康托展开进行哈希,可以参考康托展开;判断是否有解的方法,可以参考逆序对。
最后说一下,一定要判断没有解的情况,否则就TLE。
代码(C++):
#include <iostream>
#include <cmath>
#include <queue>
#include <cstring>
#include <cstdio>
#define N 3
#define MAX 370000
using namespace std;
const int dir[4][2]={{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
const char mov[4] = {'r', 'l', 'u', 'd'};
bool vis[MAX], number[N*N];
int num;
struct Puzzle{
int p[N][N];
int blank[2];
int g;
double f0;
int way;
int pre;
Puzzle()
{
g = 0;
way = pre = -1;
f0 = 0.0;
}
} puzzle[MAX];
struct cmp{
bool operator() (int a, int b)
{
return puzzle[a].f0 > puzzle[b].f0;
}
};
int heuristic(int puzzle[][N])
{
int x, y, num, dist=0;
for(int i=0; i<N; i++)
{
for(int j=0; j<N; j++)
{
num = puzzle[i][j];
if(num!=0)
{
x = (num-1) / N;
y = (num-1) % N;
}else{
x = N-1;
y = N-1;
}
dist += abs(x-i) + abs(y-j);
}
}
return dist;
}
Puzzle successor(int id, int x, int y, int i)
{
Puzzle node = puzzle[id];
node.p[node.blank[0]][node.blank[1]] = node.p[x][y];
node.p[x][y] = 0;
node.blank[0] = x;
node.blank[1] = y;
node.way = i;
node.g += 1;
node.f0 = node.g + heuristic(node.p);
node.pre = id;
return node;
}
bool is_goal(int puzzle[][N])
{
int x, y, num;
for(int i=0; i<N; i++)
{
for(int j=0; j<N; j++)
{
num = puzzle[i][j];
if(0==num)
{
x = N-1;
y = N-1;
}else{
x = (num-1) / N;
y = (num-1) % N;
}
if(x!=i || y!=j) return false;
}
}
return true;
}
int factorial(int n)
{
if(1==n||0==n) return 1;
else return n*factorial(n-1);
}
int hash_value(int puzzle[][N])
{
int a, k, seq=0, c=N*N-1;
memset(number, false, sizeof(number));
for(int i=0; i<N; i++)
{
for(int j=0; j<N; j++)
{
a = 0;
k=0;
while(puzzle[i][j]>k) //0~N*N-1
{
if(false == number[k]) a++;
k++;
}
number[k]=true;
seq += a * factorial(c);
c--;
}
}
return seq;
}
void set_visit(int puzzle[][N])
{
int seq;
seq = hash_value(puzzle);
vis[seq] = true;
}
bool is_visit(int puzzle[][N])
{
int seq;
seq = hash_value(puzzle);
return vis[seq];
}
void print_process(int id)
{
if(-1==puzzle[id].pre) return;
print_process(puzzle[id].pre);
printf("%c", mov[puzzle[id].way]);
}
bool check(int puzzle[][N]) //此处只针对8数码
{
int sum=0;
for(int i=0; i<N*N; i++)
{
if(puzzle[i/N][i%N]==0) continue;
for(int j=i+1; j<N*N; j++)
{
if(puzzle[j/N][j%N]==0) continue;
if(puzzle[i/N][i%N]>puzzle[j/N][j%N]) sum++;
}
}
if(sum%2==0) return true;
else return false;
}
int main()
{
//freopen("in.txt", "r", stdin);
int nx, ny, id;
bool find_flag;
char ch;
priority_queue<int, vector<int>, cmp> OPEN;
Puzzle node, tmp;
while(scanf("%c ", &ch)!=EOF)
{
num = 0;
find_flag = false;
memset(vis, false, sizeof(vis)); //用于判断是否访问过
for(int i=0; i<N; i++) //0代表空格
{
for(int j=0; j<N; j++)
{
if(i!=0||j!=0) scanf("%c ", &ch);
if(ch == 'x')
{
puzzle[num].p[i][j] = 0;
puzzle[num].blank[0] = i;
puzzle[num].blank[1] = j;
}else puzzle[num].p[i][j] = ch-'0';
}
}
if(!check(puzzle[num].p))
{
printf("unsolvable\n");
continue;
}else{
puzzle[num].f0 = puzzle[num].g + heuristic(puzzle[num].p);
}
set_visit(puzzle[num].p);
OPEN.push(num);
num++;
while(!OPEN.empty())
{
id = OPEN.top();
node = puzzle[id];
OPEN.pop();
if(is_goal(node.p))
{
print_process(id);
printf("\n");
find_flag = true;
break;
}
for(int i=0; i<4; i++)
{
nx = node.blank[0] + dir[i][0];
ny = node.blank[1] + dir[i][1];
if(nx>N-1||nx<0||ny>N-1||ny<0) continue;
tmp = successor(id, nx, ny, i);
if(!is_visit(tmp.p))
{
set_visit(tmp.p);
puzzle[num] = tmp;
OPEN.push(num++);
}
}
}
while(!OPEN.empty()) OPEN.pop();
if(!find_flag) printf("unsolvable\n");
}
return 0;
}
题目[http://acm.hdu.edu.cn/showproblem.php?pid=1043]:
Eight
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Special Judge
Problem Description
The 15-puzzle has been around for over 100 years; even if you don’t know it by that name, you’ve seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let’s call the missing tile ‘x’; the object of the puzzle is to arrange the tiles so that they are ordered as:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 x
where the only legal operation is to exchange ‘x’ with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle:
1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4
5 6 7 8 5 6 7 8 5 6 7 8 5 6 7 8
9 x 10 12 9 10 x 12 9 10 11 12 9 10 11 12
13 14 11 15 13 14 11 15 13 14 x 15 13 14 15 x
r-> d-> r->
The letters in the previous row indicate which neighbor of the ‘x’ tile is swapped with the ‘x’ tile at each step; legal values are ‘r’,’l’,’u’ and ‘d’, for right, left, up, and down, respectively.
Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and
frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing ‘x’ tile, of course).
In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three
arrangement.
Input
You will receive, several descriptions of configuration of the 8 puzzle. One description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus ‘x’. For example, this puzzle
1 2 3
x 4 6
7 5 8
is described by this list:
1 2 3 x 4 6 7 5 8
Output
You will print to standard output either the word “unsolvable”, if the puzzle has no solution, or a string consisting entirely of the letters ‘r’, ‘l’, ‘u’ and ‘d’ that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line. Do not print a blank line between cases.
Sample Input
2 3 4 1 5 x 7 6 8
Sample Output
ullddrurdllurdruldr