• POJ 1077 Eight【八数码问题】


    http://poj.org/problem?id=1077
    八数码问题:
    由1,2,3,4,5,6,7,8,x组成一个3*3的矩阵,如
    1 2 3
    4 x 5  
    6 7 8
    其中x可与其上,下,左,右相邻的元素互换,
    现问从给出状态出发到达以下状态:
    1 2 3
    4 5 6
    7 8 x
    需要对x进行怎样的位移操作,输出x的最少位移信息,
    若状态不可达,输出unsolvable

    分析:
    1.一个很容易的想法,即BFS,
    2.用康托展开将每个状态转化为整数,即可略过已访问点
    3.再用优先队列加以优化
    4.其实可以用A*加以优化,f(x) = g(x)+h(x),h(x)为当前状态中x所在位置到右下角位置的哈密尔顿距离(不过实际中没遇到)
    5.如果是单case的话,用以上即可水过,
      但若是多case,复杂度会过高,超时。
    6.解决多case的方法是,在预处理中从目标状态出发BFS,记录所有可达状态信息。
      这样,对于输入的任何一种状态,便可直接输出答案(又邪恶了吧。。)

    PS:
    问题在BFS过程略过已搜索过的状态
    纠结了很久之后,终于还是不忍去网上搜了一下,
    呵呵,居然让我发现还有康托展开这么个好东东!
    关于康托展开,百度百科上说得还是比较清楚的:http://baike.baidu.com/view/437641.htm
    简单说一下:
      将x抽象为数字9,对于每个状态,可映射为{1,2,3,4,5,6,7,8,9}的一个排列。
      根据康托展开,即将具体的排列转化为一个数字,即可保存其状态

    这个该死的康托,还是用了一定的时间去搞的,可惜,我只知道怎样把一个排列变成数字,把数字变成排列是怎么也搞不来,
    所以,偷偷小懒,干脆把数组扔进了节点里,邪恶了。。。

    因为POJ上是单case的,所以果断从给出的状态BFS直到搜到最终状态为止,就这样邪恶的水过。。
    ZOJ和HDU貌似很是不给面子,多case,果断超时。。。。
    回来继续研究,赫,不让我怎么搞,我就倒着搜呗,于是想到了在预处理中从目标点BFS一次,记录下所有可达状态,O(∩_∩)O哈,就这么水水地又过了。。

    Code BFS:

    View Code
    #include<stdio.h>
    #include
    <string.h>
    #include
    <queue>
    usingnamespace std;
    constint N =10;
    constint Upper =362880+1;
    bool visited[Upper];
    int pre[Upper];
    char ppre[Upper];
    int mod[9]={40320,5040,720,120,24,6,2,1,1};
    int dxy[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
    char ansstr[Upper];
    struct node
    {
    int status;
    int step;
    int array[9];//记录当前点地图
    booloperator<(const node &A)const
    {
    return step>A.step;
    }
    };
    int a[N];

    int getStatus()
    {
    int ans =0;
    int i,j;
    for(i=0;i<=8;i++)
    {
    int tn =0;
    for(j=i+1;j<9;j++)
    if(a[j]<a[i])tn++;
    ans
    =ans+tn*mod[i];
    }
    return ans;
    }
    void outputStatus()
    {
    int i;
    for(i=0;i<9;i++)
    printf(
    "%d,",a[i]);
    printf(
    "\n");
    }

    /*
    从目标状态即(1,2,3,4,5,6,7,8,x)出发,BFS,标记所有可达点
    */
    void BFS()
    {
    memset(visited,
    false,sizeof(visited));
    node cur;
    int i;
    cur.step
    =0;
    i
    =0;
    for(i=0;i<9;i++)
    a[i]
    =i+1;
    cur.status
    = getStatus();
    for(i=0;i<9;i++)
    cur.array[i]
    =a[i];

    pre[cur.status]
    =-1;
    priority_queue
    <node>Q;
    Q.push(cur);
    visited[cur.status]
    =true;
    node next;
    while(!Q.empty())
    {
    cur
    = Q.top();
    Q.pop();
    int nine =0;
    int ta[10];
    for(i =0;i<9;i++)
    {
    a[i]
    =cur.array[i];
    ta[i]
    =a[i];
    if(ta[i]==9)nine = i;
    }
    int ni = nine/3;
    int nj = nine%3;
    for(i=0;i<4;i++)
    {
    int ti = ni+dxy[i][0];
    int tj = nj+dxy[i][1];
    if(ti>=0&&ti<3&&tj>=0&&tj<3)
    {
    int tp = ti*3+tj;
    a[tp]
    =ta[nine];
    a[nine]
    =ta[tp];
    next.status
    = getStatus();
    next.step
    =cur.step+1;
    if(visited[next.status]==false)
    {
    int kk ;
    for(kk=0;kk<9;kk++)
    next.array[kk]
    =a[kk];
    Q.push(next);
    visited[next.status]
    =true;
    pre[next.status]
    =cur.status;
    switch(i)
    {
    case0:ppre[next.status] ='d';break;
    case1:ppre[next.status] ='u';break;
    case2:ppre[next.status] ='r';break;
    case3:ppre[next.status] ='l';break;
    }
    }
    a[tp]
    =ta[tp];
    a[nine]
    =ta[nine];
    }

    }
    }

    }

    int main()
    {
    BFS();
    while(true)
    {
    int i;
    for(i=0;i<9;i++)
    {
    char c ='';
    while(c!='x'&&(c<='0'||c>'9'))
    {
    if((c=getchar())==EOF)return0;
    }
    if(c=='x')
    a[i]
    =9;
    else
    a[i]
    =c-'0';
    }
    int status = getStatus();
    if(visited[status]==false)
    printf(
    "unsolvable\n");
    else
    {
    int kk =0;
    i
    =status;
    while(pre[i]!=-1)
    {
    printf(
    "%c",ppre[i]);
    i
    =pre[i];
    }
    printf(
    "\n");
    }
    }
    return0;
    }


    IDA*
    很巧妙的方法,从人家http://www.cnblogs.com/liyongmou/archive/2010/07/19/1780861.html那里偷来的,个人觉得很不错,也在这里贴一下,(*^__^*) 嘻嘻……

    IDA*

    View Code
    #include<iostream>
    #include
    <math.h>
    #include
    <stdio.h>
    usingnamespace std;
    constint SIZE =3;
    char board[SIZE][SIZE];
    //启发函数,除去x之外到目标网格的距离和
    int goal_state[9][2]={{0,0},{0,1},{0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}};
    int step[4][2] = {{-1,0},{0,-1},{0,1},{1,0}};//ulrd
    char op[5] ="ulrd";

    int h(char board[][SIZE])
    {
    int cost =0;
    int i,j;
    for(i=0;i<SIZE;i++)
    for(j=0;j<SIZE;j++)
    {
    if(board[i][j]!=SIZE*SIZE)
    cost
    += abs(i - goal_state[board[i][j]-1][0]) +
    abs(j
    - goal_state[board[i][j]-1][1]);
    }
    return cost;
    }
    inline
    int min(int a,int b)
    {
    return a<b?a:b;
    }
    char solution[1000];
    int bound;//上界
    bool ans;//是否找到答案
    //DFS返回next_bound
    int DFS(int x,int y,int dv,char pre_move)
    {
    int hv = h(board);
    if(hv+dv>bound)
    return hv+dv;
    if(hv ==0)
    {
    ans
    =true;
    return dv;
    }

    int next_bound = 1e9;
    int i;
    for(i=0;i<4;i++)
    {
    if(i+pre_move ==3)//与上一步相反的移动
    continue;
    int nx = x+step[i][0];
    int ny = y+step[i][1];
    if(0<=nx&&nx<SIZE&&0<=ny&&ny<SIZE)
    {
    solution[dv]
    =i;
    swap(board[x][y],board[nx][ny]);
    int new_bound = DFS(nx,ny,dv+1,i);
    if(ans)
    return new_bound;
    next_bound
    = min(next_bound,new_bound);
    swap(board[x][y],board[nx][ny]);
    }
    }
    return next_bound;
    }

    void IDA_star(int sx,int sy)
    {
    ans
    =false;
    bound
    = h(board);//初始化代价
    while(!ans&&bound<=100)//上限
    {
    bound
    = DFS(sx,sy,0,-10);
    }
    }
    int main()
    {
    int sx,sy;
    char c;
    int i,j;
    for(i=0;i<SIZE;i++)
    for(j=0;j<SIZE;j++)
    {
    cin
    >>c;
    if(c=='x')
    {
    board[i][j]
    =SIZE*SIZE;
    sx
    = i;
    sy
    = j;
    }
    else
    board[i][j]
    = c -'0';
    }

    IDA_star(sx,sy);

    if(ans)
    {
    for(i=0;i<bound;i++)

    cout
    <<op[solution[i]];

    }
    else
    cout
    <<"unsolvable";
    return0;
    }
     
  • 相关阅读:
    【python3.8】斐波拉契数列实现
    【Java开发基础】计算两个毫秒之间相差多少天
    FileZilla关闭更新检测
    【Java开发基础】生成两个正数之间的随机数
    Thinkphp6笔记十九:加载自定义配置
    linux系统安装坚果云
    vim NERDTree 目录插件常见用法
    vim 插件管理
    vim 窗口分割 以及 tab常用操作
    vim Ntree 树形目录常见用法
  • 原文地址:https://www.cnblogs.com/AndreMouche/p/1969297.html
Copyright © 2020-2023  润新知