• 八数码问题增强版(A*算法)


    题目

    Description

        三行三列的数组,其元素值为0至8的数。现有如下的变换规则:

        1: 将0与上面一行元素对换

        2:将0与下面一行元素对换

        3:将0与左面一行元素对换

        4:将0与右面一行元素对换

        如果已知一个三行三列元素的初始情况,问最少需几次变换,能变换为指定的一种情况?

    Input

    包括六行的数据,每行有三个以空格分隔的数字。 前三行为原始状态 后三行为目标状态

    Output

    若能在20次以内(包括20)变换得到目标状态的数组,输出最少的变换次数; 
    若不能在20次以内(包括20)变换得到目标状态的数组,输出No solution!

    Sample Input

    0 4 8
    2 6 3
    1 7 5
    0 2 3
    1 8 4
    7 6 5

    Sample Output

    10

     

    解题思路:

    也就是暴力去搜索,暴力查看当前状态是否与目标状态一致;

    枚举假设的答案,看是否满足条件,如果满足在假设交换次数的情况下能到达目标状态,那么假设的答案就是正解;

    所以可以从1-20去枚举假设的交换次数,我们只需验证答案即可;

    但暴力铁定是过不了的,所以需要优化;

    优化思路:

    假设当前状态为

    0 1 2

    3 4 5

    6 7 8

    目标状态为

    1 2 0

    3 4 5

    6 7 8

    那么最理想的交换次数就是3次,也就是比较目标状态与当前状态 有多少个不一样的数字;

    这样就可以得出从当前状态到目标状态的最少交换次数,也是最理想的交换次数;

    然鹅,并不是所有当前状态到目标状态的最少交换次数都是 求有多少个不一样的数字;

    如:

    当前状态

    0 1 2

    3 4 5

    6 7 8

    目标状态

    7 1 2

    3 4 5

    0 8 6

    很显然这个当前状态达到目标状态,肯定是大于最理想的交换次数的,但绝不会小于最理想的交换次数;

    所以我们可以利用这个定性来优化,也就是如果   当前交换的次数+最理想的交换次数>假设的交换次数 

    那么说明我们假设的这个答案不满足条件,那么重新在假设一个交换次数进行验证

    渣渣代码(包涵注释)

    #include<bits/stdc++.h>
    int ans[4][4],a[4][4],flag,k;
    int bx[4]={0,1,-1,0};
    int by[4]={1,0,0,-1};
    using namespace std;
    int check()
    {
        for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
        if(ans[i][j]!=a[i][j])return 0;//查看当前状态是否与目标状态一致 
        return 1;
    }
    int test(int step)//理想状态的优化,上面题解说明中给出 
    {
        int t=0;
        for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
        if(ans[i][j]!=a[i][j])
        { if(++t+step>k) return 0;}
        return 1;
    }
    void A_star(int step,int x,int y,int pre)
    {
        if(step==k){ if(check())flag=1; return;}//如果假设的答案变换次数内可以到达目标状态,
                                               //flag=1 说明找到答案 
        if(flag) return; //如果找到答案了就返回 
        if(step>k)return;//如果变换的次数超过,假设的答案,那么也返回 
        if(step>20)return;//超过题目给的界限,返回 
        for(int i=0;i<4;i++)//枚举每次变换的方向 
        {
            int nx=x+bx[i],ny=y+by[i];//记录与0交换的位置 
            if(nx<1||nx>3||ny<1||ny>3||pre+i==3)//不越界 且 不走回头路
                                               //如 by[0]=1,by[3]=-1,by[0]+by[3]=0,
                            //说明当前状态 0 3 交换成后 3 0,又会交换成 0 3 ,我们要防止这种情况 
                continue;
            swap(a[x][y],a[nx][ny]);//交换 
            if(test(step)&&!flag)//如果没有找到答案并且可以往下找 
                A_star(step+1,nx,ny,i);
            swap(a[x][y],a[nx][ny]);//回溯 
        }
    }
    int main()
    {
        int x,y;
        for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
        {
            cin>>a[i][j];
            if(a[i][j]==0)
                x=i,y=j;//记录下0在那个位置 
        }
        for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++)
            cin>>ans[i][j];//将目标状态 输入 
        if(check()){cout<<0;return 0;}//如果目标状态与原始状态相等,就不需变换了 
        while(++k)
        {//枚举假设每个答案 k 
            A_star(0,x,y,-1);
            if(k>20){cout<<"No solution!";return 0;}
            if(flag){cout<<k;break;}//如果假设的答案成立,就输出 
        }
        return 0;
    }
  • 相关阅读:
    Windows 显示隐藏文件
    Python 程序一行代码解决乘法口诀表
    【转发】基于Bert-NER构建特定领域的中文信息抽取框架(上)
    【转发】GET和POST两种基本请求方法的区别
    【转发】实现yolo3模型训练自己的数据集总结
    第十章集合总结
    2016-2017 201671010134 异常处理
    JAVA基础编程
    2016-2017 201671010134 第六章总结
    java总结
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/12945681.html
Copyright © 2020-2023  润新知