P1379 八数码难题

A*

A*(念做:A Star)算法是一种很常用的路径查找和图形遍历算法,相比于bfs有较好的性能和准确度。

定义比较函数

[f(n)=g(n)+h(n) ]

其中(g(n))为节点(n)距离起点的代价,(h(n))为启发函数,表示预估代价,(f(n))则表示节点的优先级。我们每次选择节点时都优先选择(f(n))最小的节点拓展路线

设定两个集合(A,B),分别存贮待遍历的节点与已经遍历过的节点

对于网格图来说,如果只允许上下左右移动,则启发函数(h(x))可以为两点之间的曼哈顿距离

如果八个方向,则可以用对角距离

任意方向,则使用欧几里得距离

IDA*

迭代加深搜索

每次改变搜索深度,直到搜到答案

P1379 八数码难题

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

这题可以双向bfs

A*

IDA*

由于这道题答案范围不会太大,可以考虑IDA*

启发函数h为所有数字离正确位置的曼哈顿距离,每次增加搜索深度,搜到则出解

#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
const int d[4]={-1,0,0,1},f[4]={0,-1,1,0},w[9]={2,1,1,1,2,3,3,3,2},z[9]={2,1,2,3,3,3,2,1,1};
int ans=-1,flag,a[4][4];

int guess()
{
    int i,j,sum=0;
    for (i=1;i<4;i++)
        for (j=1;j<4;j++)
            if (a[i][j]) sum+=abs(w[a[i][j]]-i)+abs(z[a[i][j]]-j);
    return sum;
}

void dfs(int now,int x,int y)
{
    if (flag) return;
    if (now>ans) return;
    int v=guess(),i,m,n;
    if (v+now>ans) return;
    if (!v) { flag=1; return; }
    for (i=0;i<4;i++)
    {
        m=x+d[i],n=y+f[i];
        if (m && m<4 && n && n<4)
        {
            swap(a[x][y],a[m][n]);
            dfs(now+1,m,n);
            swap(a[x][y],a[m][n]);
        }
    }
}

int main()
{
    int i,j,x,y;
    for (i=1;i<4;i++)
        for (j=1;j<4;j++)
        {
            a[i][j]=getchar()-48;
            if (!a[i][j]) x=i,y=j;
        }
    while (!flag) ans++,dfs(0,x,y);
    printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/lcezych/p/12678818.html