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

原创 2012年03月24日 12:48:48
#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;
}

POJ 1077 八数码 三种解法

POJ 1077 Eight题目链接:http://poj.org/problem?id=1077
  • lvxin1204
  • lvxin1204
  • 2016年05月31日 13:41
  • 1280

八数码问题-8重境界

八数码的八境界     研究经典问题,空说不好,我们拿出一个实际的题目来演绎。八数码问题在北大在线测评系统中有一个对应的题,题目描述如下: Eight Time Limit: 1000...
  • wind_2008_06_29
  • wind_2008_06_29
  • 2015年08月28日 22:01
  • 1015

回溯法解决八数码问题python

这次人工智能的作业就是用回溯法解决八数码问题,经过一天多的功夫,终于写出来了。下面是正题回溯法是人工智能领域的一种重要的盲目搜索算法,何为盲目算法,即是基于规则,不断的尝试可能的路径,直到到达目的的解...
  • sinat_32290679
  • sinat_32290679
  • 2017年04月11日 21:18
  • 1296

用A*算法解决八数码问题 MATLAB

代码: bashuma.m function []=bashuma global e; %open表计数 global i; %close表计数 global m; %循环次数计数 ...
  • ljd939952281
  • ljd939952281
  • 2017年04月18日 16:58
  • 1356

八数码问题的过程表示及其实现

过程式知识表示是将有关某一问题领域的知识, 连同如何使用这些知识的方法,均隐式的表达为 一个求解问题的过程,每个过程是一段程序,完 成对具体情况的处理。过程式不像陈述式那样具有固定的形式,如何描述知识...
  • baidu_36193246
  • baidu_36193246
  • 2017年04月13日 22:32
  • 1175

POJ 1077 Eight(八数码第八境界|IDA*+曼哈顿距离+判断是否有解)

题意:八数码。 思路:IDA*+曼哈顿距离+判断是否有解。因为要用到IDA*搜索,所以我们搜索之前先判断一下是否有解。 判断的方法是学习一个大神的: 判断八数码问题是否有解  IDA*比起B...
  • u014664226
  • u014664226
  • 2016年01月23日 01:54
  • 699

八皇后问题不同思路解答

八皇后问题不同思路解答:算法提出:​在国际象棋棋盘上(8*8)放置八个皇后,使得任意两个皇后之间不能在同一行,同一列,也不能位于同于对角线上。问共有多少种不同的方法,并且指出各种不同的放法。思路一:回...
  • u014595375
  • u014595375
  • 2016年10月29日 01:49
  • 368

A*算法解决八数码问题(C++版本)

八数码问题定义: 八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问...
  • yums467
  • yums467
  • 2016年03月09日 11:34
  • 2148

每天刷个算法题20160526:BFS解决八数码问题(九宫格问题)

为了防止思维僵化,每天刷个算法题。这里是BFS解决八数码问题(九宫格问题)。...
  • u012077163
  • u012077163
  • 2016年05月28日 14:46
  • 4158

八数码宽度优先算法

相关代码要感谢网上的一些大神给予的分享 、一、程序思路 结构体map 用来作为一个结点的数据结构。 PrintMap 用来打印一个结点 MoveMap 函数用来扩展一个结点的后继结点(上、下、...
  • u014050788
  • u014050788
  • 2015年11月29日 17:52
  • 1725
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:【POJ1077】Eight 八数码问题,解题报告+思路+代码
举报原因:
原因补充:

(最多只允许输入30个字)