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

#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;
}

你可能感兴趣的:(【POJ1077】Eight 八数码问题,解题报告+思路+代码)