P1379 八数码难题

题目大意:给你一个矩阵,问最少要几步将矩阵变为123804765,每次操作可将0与上下左右各个数调换。

思路:这道题深搜深度非常大,广搜分类太多,导致无论深搜还是广搜都会TLE,那么我们应该找一个综合广搜和深搜优点的算法——迭代加深搜索。我们枚举移动几次会到达目标节点,一旦搜到次数大于枚举的数就return。

但裸的迭代加深搜索可能过不了,所以这时就需要一个更强力的做法:IDA*。在我理解,它其实很像对迭代加深搜索的剪枝:你预估一下当前情况到目标情况所需的步数,如果这个步数加上你已经枚举的深度是大于迭代的值的话就return;而这个预估的值就是h函数,他必须要比实际值小,但越接近实际值越好,所以IDA*的搜索复杂度取决于h函数的设定。

这里我们设当前矩阵的点和目标矩阵所对应的点不同的个数为h函数,易证这个函数是正确的,但却不是最优的,复杂度O(可过),800多ms跑得飞快。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
const int MaxN=10;
int n[MaxN][MaxN],a[MaxN][MaxN],nx,ny,now;
int ffx[5]={0,1,-1,0,0},
    ffy[5]={0,0,0,1,-1};
int calh(){
    int sum=0;
    for(int i=1;i<=3;++i){
        for(int j=1;j<=3;++j){
            if(a[i][j]!=n[i][j]) ++sum;
        }
    }
    return sum;
}
int dfs(int xx,int yy,int x,int y,int id){
    if(now+calh()-1>id) return 0;//减去空格移动的1
    if(calh()==0) return 1;
    for(int i=1;i<=4;++i){
        int dx=x+ffx[i],dy=y+ffy[i];
        if(dx<=0||dx>3||dy<=0||dy>3||(xx==dx&&yy==dy)) continue;
        ++now;swap(a[x][y],a[dx][dy]);if(dfs(x,y,dx,dy,id)) return 1;
        now--;swap(a[x][y],a[dx][dy]);
    }
    return 0;
}
int main(){
    char c[MaxN];
    n[1][1]=1;n[1][2]=2;n[1][3]=3;
    n[2][1]=8;n[2][2]=0;n[2][3]=4;
    n[3][1]=7;n[3][2]=6;n[3][3]=5;
    for(int i=1;i<=9;++i) cin>>c[i];
    a[1][1]=c[1]-'0';a[1][2]=c[2]-'0';a[1][3]=c[3]-'0';
    a[2][1]=c[4]-'0';a[2][2]=c[5]-'0';a[2][3]=c[6]-'0';
    a[3][1]=c[7]-'0';a[3][2]=c[8]-'0';a[3][3]=c[9]-'0';
    for(int i=1;i<=3;++i) for(int j=1;j<=3;++j) if(a[i][j]==0) nx=i,ny=j;
    for(int i=1;i<=1e5;++i){
        now=0;
        if(dfs(0,0,nx,ny,i)) break;
    }
    printf("%d
",now);
}
原文地址:https://www.cnblogs.com/X-rice/p/11219384.html