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;
    }
    
  • 相关阅读:
    CREATE AGGREGATE
    技术文档列表
    jQuery 判断表单中多个 input text 中至少有一个不为空
    Java实现 蓝桥杯 算法提高 奥运会开幕式
    Java实现 蓝桥杯 算法提高 最长滑雪道
    Java实现 蓝桥杯 算法提高 最长滑雪道
    Java实现 蓝桥杯 算法提高 最长滑雪道
    Java实现 蓝桥杯 算法提高 最大值路径
    Java实现 蓝桥杯 算法提高 最大值路径
    Java实现 蓝桥杯 算法提高 最大值路径
  • 原文地址:https://www.cnblogs.com/akura/p/10877907.html
Copyright © 2011-2022 走看看