您的位置:首页 > 编程语言

【POJ1077】Eight 八数码问题,解题报告+思路+代码

2012-03-24 12:48 435 查看
#include <cstring>
#include <cstdlib>
#include <cstdio>
#define INPUT
using namespace std;
/**
Problem : poj1077,hdu1043,经典的八数码问题。
知识点: BFS + HASH + 打表 + 父亲节点记录
境界:3 - BFS+HASH+打表

A了两天!!!
记得拿启发式,双向BFS重新写一遍
IDA*就不做要求了。。。
思路:
1.打表 + 输出
将每个状态记录成0..9的一个排列。0代表的是'x'的位置。
每个状态用康托展开,求出自己的唯一id,然后搜索的下一个状态的
fa[id_next] = id_now,然后move[id_next] = i;
dis[id_next] = dis[id_now] + 1;
这样,根据目标状态,求出其对应的康托展开,然后根据
fa[]数组就能找到每个移动。dis是指从1234567890到目标节点的最短steps
根据CLRS,BFS能找到两个节点中的最短路径。

注意边界,我们是反向搜索的,从目标状态往回搜索是这么搜索的:
int j = id_dest;
for(int k = dis[id_dest]; k > 0 ; k--,j = fa[j] )
{
printf("%d",move[j]);
}
因为达到了dest 就不再移动,所以move[id_dest]记录的是最后一次的移动
一点一点往前输出也就是了,但是注意记录的是从"1234567890"移动到目标
状态的方式,所以对于 1234567890 的向左移动,在这里就变成了向右移动
(也就是说,把整个过程反过来),所以要反着输出。
2.搜索
搜索的时候判重问题,重复的状态不再进行搜索,利用hash表进行搜索,
按照PP的解释这样查询的效率是O(1)
每个状态看做一个整数,然后
hash_id = nownum % HASHKEY;
注意hash表下标一定要从1开始!!!
也就是下文的now从1开始!否则next[hash_id]可以为0!!那就恶心死了!
while(next[hash_id])
{
if states[ next[hash_id] ] == nownum return 0; //插入失败
hash_id = next[ hash_id ];
}
next[ hash_id ] = now;
states[now] = nownum;
now++;
return true;
如果hashkey相同且发现了相同的状态,那么就返回失败,如果返回失败,那么就不展开该节点进行搜索了。

教训:
这题正向不行就应该想到打表,尤其是康托展开+逆序数的应用是一个亮点!详细看
Cantor那个代码。cantor[]这个数组还记录了排列所需要用的阶乘数目。
这道题双向BFS,A*,IDA*都能做,所以是一道好题,第三重境界,还需要加油!
还有输出的反向输出,父节点的记录,各个都是那么巧妙,这道题要多看几遍

*/
const int c0de4fun = 364880;
const int HASHKEY =  1000003;
//////////////////l r u d
//////////////////r,l,d,u
const int dx[] = {0,0,-1,1};
const int dy[] = {-1,1,0,0};
const int cantor[] = {1,1,2,6,24,120,720,5040,40320};
//int vals[c0de4fun];
int head[HASHKEY];
int next[HASHKEY];
int states[HASHKEY];
int queue[c0de4fun];
int dis[c0de4fun];
int fa[c0de4fun];
int move[c0de4fun];
int now = 1,front = 1 ,rear = 2;
int _dest[9];
int hashk(int node)
{
return node % HASHKEY;
}
bool insert(int node)
{
int key = hashk(node);
while ( next[key] )
{
if( states[ next[key] ] == node  ) return false;
key = next[key];
}
next[key] = now;
states[now] = node;
now++;
return 1;
}
void segNum(int* tmp,int node)
{
for(int i = 8 ; i >= 0 ; i--)
{
tmp[i] = node % 10;
node /= 10;
}
}
int getNum(int* tmp)
{
int res = 0;
for(int i = 0 ; i < 9 ; i++)
{
res = res*10 + tmp[i];
}
return res;
}
int getCanto(int *a)
{
int ans = 0 ;
int i,j,cnt;
for( i = 0 ; i <  9 ;i++)
{
cnt = 0;
for( j = i + 1; j < 9; j++)
{
// if(a[i] < a[j]) cnt++;
if( a[i] > a[j] ) cnt++;
}
ans = ans + cantor[ 8 - i ] * cnt;
}
return ans;
}
void bfs(int node)
{
int i,x,y,newx,newy,z,newz;
int tmp[9];
int tmp1[9];
int tnode,tnode1;
//   int front = 1 ,rear = 2;
fa[0] = 0;
dis[0] = 0;
queue[front] = node;
insert(node);
while(front < rear)
{
tnode = queue[front];
//insert(tnode);
segNum(tmp,tnode);
for( i = 0 ; i < 9; i++)
{
if ( tmp[i] == 0 ) break; //找到X的位置
}
x = i / 3 ; y = i % 3;
z = i;
for( i = 0 ; i < 4 ; i++)
{
newx = x + dx[i];
newy = y + dy[i];
if( newx >= 0 && newx < 3 && newy >=0 && newy < 3)
{
///合法移动
newz = newx * 3 + newy;
memcpy(tmp1,tmp,sizeof(tmp));
tmp1[newz] = tmp[z];
tmp1[z] = tmp[newz];
tnode = getNum(tmp1);
if ( insert(tnode) )
{
int cant_tmp = getCanto(tmp);
int cant_tmp1 = getCanto(tmp1);
///这里用康托展开给tnode一个值,然后
///也给tnode1一个值,fa[kan_tnode1] = kan_node;
///然后move[kan_node] = 对应值。
fa[cant_tmp1] = cant_tmp;
move[cant_tmp1] = i;
dis[cant_tmp1] = dis[cant_tmp] + 1;
queue[rear] = tnode;
rear++;
}
}
}
front++;
}
#ifdef TEST
for(int i = 0 ; i < 9 ; i++)
{
printf("%d ",tmp[i]);
}
#endif
}
void Solve()
{
int k = getNum(_dest);
int l = 0;
int ind;
bool isSoluted = false;
for(int i = 1 ; i < rear; i++)
{
if ( k == queue[i] )
{
// printf("Can be solved\n");
isSoluted = true;
break;
}
}
if( isSoluted )
{
ind = getCanto(_dest);
l = ind;
for( k = dis[ind] ;k > 0;k--,l = fa[l])
{
switch(move[l])
{
case 0 :
// printf("l ");
printf("r");
break;
case 1:
//  printf("r");
printf("l");
break;
case 2:
//    printf("u ");
printf("d");
break;
case 3:
printf("u");
//    printf("d ");
break;
}
//  printf("%d ",move[k]);
}
printf("\n");
// printf("Solved\n");*/
}
else
{
printf("unsolvable\n");
}
}
int main()
{
char tmp1;
int i = 0;
#ifdef INPUT
freopen("b:\\acm\\poj1077\\input.txt","r",stdin);
#endif
bfs(123456780);
while ( scanf("%c",&tmp1) != EOF )
{
if (tmp1 == ' ' || tmp1 == '\n') continue;
if (tmp1 == 'x' )
{
_dest[i] = 0;
}
else
{
_dest[i] = tmp1 - '0';
}
i++;
if( i % 9  == 0)
{
Solve();
i = 0;
}

}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐