八数码问题脚本方案

前言

前几周在hihoCoder上看到的一关于八数码的问题(#1312:搜索三-启发式搜索)。借着提示一步一步编完了脚本,关键要点在于启发式搜索算法及其启发函数的设计。

编译环境:Linux G++

详细代码

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <list>
#include <math.h>
#include <algorithm>
using namespace std;

#define COL 3

const char *splitChar = " ";

int dataCount=0;
int data[COL][COL];

typedef struct index
{
    int status[COL][COL];
    int g;
    int h;
    int f;
    bool operator==(const index &rhs)const
    {
        for(int i=0;i<COL;i++)
        {
            for(int j=0;j<COL;j++)
            {
                if(status[i][j]!=rhs.status[i][j])
                    return false;
            }
        }
        return true;
    }
    list<index> neighborStatus()
    {
        list<index> l;
        int col=-1,row=-1;
        for(int i=0;i<COL;i++)
        {
            for(int j=0;j<COL;j++)
            {
                if(status[i][j]==0)
                {
                    col=i;row=j;
                    break;
                }
            }
            if(col!=-1)
                break;
        }
        if(row>0)
        {
            index inx;
            for(int i=0;i<COL;i++)
            {
                for(int j=0;j<COL;j++)
                {
                    inx.status[i][j]=status[i][j];
                }
            }
            inx.status[col][row]=inx.status[col][row-1];
            inx.status[col][row-1]=0;
            l.push_back(inx);
        }
        if(row<COL-1)
        {
            index inx;
            for(int i=0;i<COL;i++)
            {
                for(int j=0;j<COL;j++)
                {
                    inx.status[i][j]=status[i][j];
                }
            }
            inx.status[col][row]=inx.status[col][row+1];
            inx.status[col][row+1]=0;
            l.push_back(inx);
        }
        if(col>0)
        {
            index inx;
            for(int i=0;i<COL;i++)
            {
                for(int j=0;j<COL;j++)
                {
                    inx.status[i][j]=status[i][j];
                }
            }
            inx.status[col][row]=inx.status[col-1][row];
            inx.status[col-1][row]=0;
            l.push_back(inx);
        }
        if(col<COL-1)
        {
            index inx;
            for(int i=0;i<COL;i++)
            {
                for(int j=0;j<COL;j++)
                {
                    inx.status[i][j]=status[i][j];
                }
            }
            inx.status[col][row]=inx.status[col+1][row];
            inx.status[col+1][row]=0;
            l.push_back(inx);
        }
        return l;
    }
}SearchIndex;
typedef list<SearchIndex> IndexList;

IndexList openList,closeList;
IndexList::iterator iter;

SearchIndex getMinFStatus(IndexList &indexList)
{
    iter=indexList.begin();
    IndexList::iterator it=iter;
    int min=iter->f;
    while(iter !=indexList.end())
    {
        if(iter->f<min)
            it=iter;
        iter++;
    }
    return *it;
}

int evaluate(int data[][COL])
{
    //求曼哈顿距离
    int dis=0;
    for(int i=0;i<COL;i++)
    {
        for(int j=0;j<COL;j++)
        {
            if(data[i][j]==0)
            {
                continue;
            }
            int row=(data[i][j]-1)/3;
            int col=data[i][j]-row*3-1;
            
            dis+=abs(i-row)+abs(j-col);
        }
    }
    //dis/=2;
    return dis;
}

bool isCanCompute(int data[][COL])
{
    //判断是否有解
    int sum=0;
    int copyData[COL*COL];
    for(int i=0;i<COL*COL;i++)
    {
        int row=i/3;
        int col=i-row*3;
        copyData[i]=data[row][col];
    }
    for(int i=0;i<COL*COL-1;i++)
    {
        if(copyData[i]==0)
            continue;
        for(int j=i;j<COL*COL;j++)
        {
            if(copyData[j]==0)
                continue;
            if(copyData[j]<copyData[i])
            {
                sum++;
            }
        }
    }
    return sum%2==0;
}

int search(int data[][COL])
{
    if(!isCanCompute(data))
        return -1;
    openList.clear();
    closeList.clear();
    SearchIndex start;
    for(int i=0;i<COL;i++)
    {
        for(int j=0;j<COL;j++)
        {
            start.status[i][j]=data[i][j];
        }
    }
    start.g=0;
    start.h=evaluate(start.status);
    start.f=start.g+start.h;

    openList.push_back(start);
    
    while(!openList.empty())
    {
        SearchIndex u=getMinFStatus(openList);
        if(u.h==0)
            return u.f;
        closeList.push_back(u);
        IndexList neighborList=u.neighborStatus();
        openList.remove(u);
        //cout<<"u.g:"<<u.g<<"\tu.h:"<<u.h;
        iter=neighborList.begin();
        while(iter!=neighborList.end())
        {
            IndexList::iterator it=openList.begin();
            it=find(openList.begin(),openList.end(),*iter);
            if(it!=openList.end())
            {
                //更新v的f值
                if(iter->f>iter->h+u.g+1)
                    iter->f=iter->h+u.g+1;
            }
            else{
                it=closeList.begin();
                it=find(closeList.begin(),closeList.end(),*iter);
                if(it!=closeList.end())
                {
                }
                else
                {
                    iter->g=u.g+1;
                    iter->h=evaluate(iter->status);
                    iter->f=iter->g+iter->h;
                    openList.push_back(*iter);
                }
            }
            iter++;
        }
    }
    return -1;
}

int main()
{
    cin>>dataCount;    

    int *res=new int[dataCount];
    char *s=new char[COL*2];
    for(int j=1;j<=dataCount;j++)
    {
        for(int i=1;i<=COL;i++)
        {
            for(int row=0;row<COL;row++)
            {
                cin>>data[i-1][row];
            }
        }
        res[j-1]=search(data);
    }
    for(int i=0;i<dataCount;i++)
    {
        if(res[i]==-1)
        {
            cout<<"No Solution!"<<endl;
            continue;
        }
        cout<<res[i]<<endl;
    }
    return 0;
}

要点

①启发式搜索:一般的宽度优先搜索即对每一步计算最优步,对于之前已经计算过的步用较优解替代,最短路径算法就是最好的例子。启发式搜索简单的来说就是对于计算开销,在宽度优先搜索算法计算的开销基础上加上启发函数计算的开销,即F=G+H。

G:就是普通宽度优先搜索中的从起始状态到当前状态的代价,比如在这次的问题中,G就等于从起始状态到当前状态的最少步数。

H:是由启发函数计算的一个估计的值,表示从当前状态到目标状态估计的代价(步数)。

②启发函数设计:H是由我们自己设计的,其设计的好坏决定了搜索的效率。H越大,算法运行越快。但需要注意的一个点,启发函数的值一定要小于实际从当前态到目标态的最小步骤。否则即使运行效率加快,也可能会遗漏了最优解。可以这么理解,对于F=G+H,由于我们每次搜索方向是朝着最小F走的,随着H的增大,预估的步数占比实际的步数越大,H起着引导下一步往期望最优解走的作用,当H超过实际步数,就失去了指示到最优解的可能,即存在朝着其他更小F但不是最优解的方向走的可能。

③查询最小元素:这是提升搜索性能以一个小要点。由于我们每次查询最小元素都在openList中,它是一个动态变化的数组/链表,我们可以使用最小堆优化查询效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值