zoukankan      html  css  js  c++  java
  • P1379 八数码难题

    题目描述

    在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

    输入格式

    输入初始状态,一行九个数字,空格用0表示

    输出格式

    只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)


    除了搜索还真的没有方法下手了。我们可以搜索空格如何走,那么状态就为:

    当前的棋盘,空格的位置和走过的步数。棋盘可以用全局变量来存。每次往上下左右四个方向搜即可。

    考虑剪枝

    1.上下界剪枝:

    上界:不好求

    下界:最好的情况就是——当前情况有多少个位子与目标状态不同,即

    [sum_{i=1}^{3}sum_{j=1}^{3}[g[i][j]≠des[i][j]] ]

    虽然下界对剪枝没什么用

    2.搜索顺序优化:由于一个地方可以重复走所以顺序随意。

    3.排除等效冗余:往前走一步然后往回走一步没有意义,所以我们应该避免往上一次的反方向走。

    4.最优化剪枝:设当前的答案为ans,那么当深度>ans时就没必要搜了。

    5.记忆化:日常没什么好记的

    并没有剪掉多少诶......考虑到答案很小(观察法),我们可以用迭代加深来优化DFS。

    从1开始枚举深度,由迭代加深性质可知当第一次搜索到答案时必定是最优解,所以我们没必要做最优化剪枝了。

    显然加了迭代加深之后固然能够起作用,但是我们又扔掉了一个好的剪枝——最优化剪枝,实际上效果不明显。并且剪枝已经剪不动了,当搜索到答案的深度时,我们必然会面对一个庞大的搜索树。

    由于IDFS搜到的第一个答案就是最优解,所以搜到后可以直接跳出搜索。我们只需要让程序更快地搜到最优解即可。这时就加个启发式IDA * 吧~

    设计估价函数。结合上面提到的下界,我们可以设计这样一个函数:

    [f(g[][])=sum_{i=1}^{3}sum_{j=1}^{3}[g[i][j]≠des[i][j]] ]

    当当前深度加上预估值>当前的最大深度时就不需要往下搜了。

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define maxn 4
    using namespace std;
    
    const int des[4][4]={{},{0,1,2,3},{0,8,0,4},{0,7,6,5}};
    const int dir[4][2]={{-1,0},{0,-1},{0,1},{1,0}};
    int g[maxn][maxn],ans;
    
    inline int Abs(const int &x){ return x<0?-x:x; }
    inline int evaluate(){
        int cnt=0;
        for(register int i=1;i<=3;i++) for(register int j=1;j<=3;j++) if(g[i][j]!=des[i][j]) cnt++;
        return cnt;
    }
    inline bool check(const int &x,const int &y){ return x<1||3<x||y<1||3<y; }
    void IDAstar(int x,int y,int dep,int pre_dir,const int &maxdep){
        if(dep==maxdep){ if(!evaluate()) ans=true; return; }
        for(register int i=0;i<4;i++) if(i+pre_dir!=3){
            int tx=x+dir[i][0],ty=y+dir[i][1];
            if(!check(tx,ty)){
                swap(g[x][y],g[tx][ty]);
                if(dep+evaluate()<=maxdep) IDAstar(tx,ty,dep+1,i,maxdep);
                swap(g[x][y],g[tx][ty]);
                if(ans) return;
            }
        }
    }
    
    int main(){
        int sx,sy;
        for(register int i=1;i<=3;i++){
            for(register int j=1;j<=3;j++){
                scanf("%1d",&g[i][j]);
                if(!g[i][j]) sx=i,sy=j;
            }
        }
        if(!evaluate()){ puts("0"); goto Lzs; }
        for(register int maxdep=1;;maxdep++){
            IDAstar(sx,sy,0,-1,maxdep);
            if(ans){ printf("%d
    ",maxdep); break; }
        }
        Lzs:;
        return 0;
    }
    
  • 相关阅读:
    同一个String在使用不同的charset编码的时候equals仍然是返回true吗
    request的生存期只限于服务器跳转
    flex 客户端缓存SharedObject
    flex NaN
    oracle 任务使用
    oracle 数据泵
    datagrid 用法
    Windows系统中path环境变量详解
    [转]eclipse导入V7包出现错误解决办法
    由多线程引起的map取值为null的分析
  • 原文地址:https://www.cnblogs.com/akura/p/10877907.html
Copyright © 2011-2022 走看看