八数码难题(luogu 1379)

题目描述

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

输入输出格式

输入格式:

 

输入初始状态,一行九个数字,空格用0表示

 

输出格式:

 

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

 

输入样例
283104765
输出样例
4

这是 alphar 学长带我入门广搜的例题,当时完全是抄代码,因为完全没有代码功力

当时把 codevs 的题目A掉了,现在偶然见到 luogu 有人求助这道题目,点开一看,又是不一样的方法

思路:

这道题目关键在于判重,因为如果只是一个简单的搜索,它只会是瞎跑,杂乱无章,我们要做的就是规定它向着什么方向跑,以及不让他走了半天又绕回原地

于是就有了A*算法的思路

构造一个估价函数,其实说白了就是看他离终点还差多远

通过迭代加深的方法,如果当前状态可以到达终点,我们就继续往下走

至于估价函数……意会就好,无需证明

code

#include<stdio.h> 
#include<algorithm> 
using namespace std;
bool flag;
int k,sx,sy,mp[5][5];
int plan[5][5]={{1,2,3},{8,0,4},{7,6,5}};
int dx[]={1,0,0,-1};
int dy[]={0,1,-1,0};

bool check() 
{
    for(int i=0;i<3;i++) 
        for(int j=0;j<3;j++) 
            if(plan[i][j]!=mp[i][j]) return false;
    return true;
}

bool test(int stp) {
    int cnt=0;
    for(int i=0;i<3;++i)
        for(int j=0;j<3;++j) 
            if(mp[i][j]!=plan[i][j]) if(++cnt+stp>k) return false;
    return true;
}

void star(int x,int y,int stp,int last) 
{
    if(stp==k) {
        if(check()) flag=1;
        return;
    } 
    if(flag) return;
    for(int i=0;i<4;++i) {
        int nx=x+dx[i],ny=y+dy[i];
        if(nx<0 || ny<0 || nx>2 || ny>2 || last+i==3) continue;
        swap(mp[x][y],mp[nx][ny]);
        if(test(stp)) star(nx,ny,stp+1,i);
        swap(mp[x][y],mp[nx][ny]);
    }
}

int main()
{
    for(int i=0;i<=2;i++) 
        for(int j=0;j<=2;j++) {
            char c;
            scanf("%c",&c);
            mp[i][j]=c-'0';
            if(mp[i][j]==0) sx=i,sy=j;     
        }
    if(check()) {
        printf("0");return 0;
    }
    while(++k) {
        star(sx,sy,0,-1);
        if(flag) {
            printf("%d",k);
            break;
        }
    }
    return 0;
}

 

posted @ 2018-10-27 17:19  qseer  阅读(252)  评论(0编辑  收藏  举报