学弟讲算法-图论-A* 算法求八数码(纯代码)

一、实验目的

通过A*算法求出八数码的最少移动次数和移动过程的路径

二、实验环境

CLion

三、实验过程

3.1 bashuma.h文件

//
// Created by _casper on 2019/11/10.
//
#ifndef BASHUMA_BASHUMA_H
#define BASHUMA_BASHUMA_H

#include <iostream>
#include <vector>

using namespace std;

//状态类
typedef class State {
public:
    string str;
    string father;
    int f;
    int g;
    int h;

    State(string str, string father, int g, int h);

    //更新父节点与g值
    void updateValue(const string &fathers, int gs);

    bool operator<(const State &s1) const;

    bool operator==(const State &s1) const;

} State;


//游戏类
typedef class Game {
public:
    //八数码最初状态
    string origStr;
    //八数码最终状态
    string finStr;
    vector<State> open;
    vector<State> close;
    //移动的路径
    vector<State> path;
    //可移动路径
    /**
     * 0 1 2
     * 3 4 5
     * 6 7 8
     * 0位置可到1,3处,不可移动的方向记为-1
     */
    int moves[9][4] = {{-1, -1, 1, 3},
                       {-1, 0,  2, 4},
                       {-1, -1, 1, 5},
                       {-1, 0,  4, 6},
                       {1,  3,  5, 7},
                       {-1, 2,  4, 8},
                       {-1, -1, 3, 7},
                       {-1, 4,  6, 8},
                       {-1, -1, 5, 7}};

    Game(string origStr, string finStr);

    //判断奇偶性,以便在最开始判断是否可达
    bool isOdevity();

    //按状态搜索路径
    void searchPath(State St);

    //寻找节点的坐标
    static int getStrIndex(const string &str, const vector<State> &v);

    //设置h值
    int getWeight(string str) const;

    //生成路径
    void creatPath();

    //主循环
    void start();
} Game;

#endif //static BASHUMA_BASHstatic UMA_H

3.2 bashuma.cpp文件

//
// Created by _casper on 2019/11/10.
//
#include <iostream>
#include <vector>
#include "bashuma.h"
#include <cstdio>
#include <algorithm>

extern bool flag;

State::State(string str, string father, int g, int h) {
    this->str = std::move(str);
    this->father = std::move(father);
    this->f = g + h;
    this->g = g;
    this->h = h;
}


bool State::operator<(const State &s1) const {
    return s1.f < f;
}

bool State::operator==(const State &s1) const {
    return s1.str == str;
}

void State::updateValue(const string &fathers, int gs) {
    this->father = fathers;
    this->f = gs + this->h;
    this->g = gs;
}


Game::Game(string origStr, string finStr) : origStr(std::move(origStr)),
                                            finStr(std::move(finStr)) {}

//判断两个字符的奇偶性
bool Game::isOdevity() {
    int originalSum = 0, finalSum = 0;
    for (int i = 1; i < 9; ++i) {
        int a = 0, b = 0;
        for (int j = 0; j < i; j++) {
            if (origStr[j] > origStr[i] && origStr[i] != '0')a++;
            if (finStr[j] > finStr[i] && finStr[i] != '0')b++;
        }
        originalSum += a;
        finalSum += b;
    }
    return originalSum % 2 == finalSum % 2;
}

//从当前状态搜索路径
void Game::searchPath(State St) {
    //空白位置
    int blankIndex = St.str.find('0');
    //遍历空白位置的所有可移动方向
    for (int i = 0; i < 4; i++) {
        int movePosition = moves[blankIndex][i];
        if (movePosition == -1) {
            continue;
        }
        string newStr = St.str;
        //交换空白位置与它可达位置的元素
        swap(newStr[movePosition], newStr[blankIndex]);

        //若交换位置后的字符串未在close表中索过
        if (getStrIndex(newStr, close) == -1) {
            int newStrIndex = getStrIndex(newStr, open);
            //若该字符串未在open表中,
            if (newStrIndex == -1) {
                open.emplace_back(newStr, St.str, St.g + 1, getWeight(newStr));
            }
            //若该字符串在open表中,且经过当前状态可以使路径更优
            else if (St.g + 1 < open[newStrIndex].g) {
                //将当前状态节点设为交换后状态节点的父节点,并更新g值
                open[newStrIndex].updateValue(St.str, St.g + 1);
            }
        }
        if (newStr == finStr) {
            flag = true;
            printf("ok\n");
            return;
        }

    }
    //将当前状态移出open表
    open.erase(find(open.begin(), open.end(), St));
    close.push_back(St);
}

//给定一个字符串返回它在容器内的下标
int Game::getStrIndex(const string &str, const vector<State> &v) {
    for (int i = 0, size = v.size(); i < size; ++i) {
        if (v[i].str == str) {
            return i;
        }
    }
    return -1;
}

//生成h值
int Game::getWeight(string str) const {
    //即求出当前字符串str中的每个元素str[i]在str中的下标i与str[i]在strend中的下标之差
    int sum = 0;
    for (int i = 0; i < 9; i++) {
        if (i != str.find('0')) {
            sum += abs(i - int(finStr.find(str[i])));
        }
    }
    return sum;
}

//显示移动的路径
void Game::creatPath() {
    vector<State> v;
    v.insert(v.end(), open.begin(), open.end());
    v.insert(v.end(), close.begin(), close.end());
    State St = v[getStrIndex(finStr, v)];
    while (St.father != " ") {
        path.push_back(St);
        //找寻下一个节点
        St = v[getStrIndex(St.father, v)];
    }
    path.emplace_back(origStr, " ", 0, 0);
    reverse(path.begin(), path.end());
}


void Game::start() {
    if (!isOdevity()) {
        cout << "不可达" << endl;
        return;
    }
    State St = State(origStr, " ", 0, 0);
    open.push_back(St);
    searchPath(St);
    while (!open.empty()) {
        if (flag) {
            break;
        }
        //按f值从大到小排序
        sort(open.begin(), open.end());
        //选择f值最小的节点进行搜索
        searchPath(open[open.size() - 1]);
    }
    creatPath();
}

3.3 main.cpp文件

#include <cstdio>
#include "bashuma.h"

using namespace std;

bool flag;


int main(int argv, char *argc[]) {
    string a1 = "123456078";
    string b1 = "123456780";
    Game game = Game(a1, b1);
    game.start();
    printf("共移动%d步\n", game.path.size()-1);
    for(auto & i : game.path){
        printf("%s\n",i.str.c_str());
    }
    return 0;
}

四、运行结果


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
include using namespace std; struct node{ int nodesun[4][4]; int pre; //上一步在队列中的位置 int flag ; //步数标识,表示当前的步数为有效的 int value; //与目标的差距 int x,y; //空格坐标 }queue[1000]; //移动方向数组 int zx[4]={-1,0,1,0}; int zy[4]={0,-1,0,1}; //当前步数 int top; int desti[4][4];//目标状态 int detect(struct node *p)//检查是否找到 {int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) return 0; return 1; } //打印 void printlj() {int tempt; int i,j; tempt=top; while(tempt!=0) { for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<queue[tempt].nodesun[i][j]; if(j==3) cout<<" "<<endl; } tempt=queue[tempt].pre; } } //现在状态与目标状态有多少个不同位置 int VALUE(struct node *p) {int count=0; int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) count++; return count; } void main() { //初始化 int i,j,m,n,f; int min=10; int temp,find=0,minnumber; top=1; for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; queue[1].nodesun[i][j]=temp; } cout<<"请输入初始状态的空格的位置(行)"<>temp; queue[1].x=temp; cout<<"请输入初始状态的空格的位置(列)"<>temp; queue[1].y=temp; queue[1].value=VALUE(&queue[1]); queue[1].pre=0; //上一步在队列中的位置 queue[1].flag=0; //目标状态 for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入目标状态第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; desti[i][j]=temp; } //根据估价函数 while(!find&&top>0) { for(i=1;i<=top;i++) //////////////////////////////////////////// //min为上一图中与目标图有多少个元素不相同,queue[i]为当前图与目标图有多少个元素不相同通过这两个数的比较,就可以得出当前图较之上一图向目标图接近同时把当前的i记录下来进行下一步比较 {if(queue[i].value<min&&queue[i].flag==0) {minnumber=i;// min=queue[i].value; //还有多少不同的位数 } } queue[minnumber].flag=1; //表示此位有效 ////////////////////////////////////// // for(f=0;f=1&&i=1&&j<=3) {top++; ///////////////////////////////////////////// //位置交换 queue[top]=queue[minnumber]; queue[top].nodesun[m][n]=queue[minnumber].nodesun[i][j]; queue[top].nodesun[i][j]=0; /////////////////////////////////////// //空格移动方向 queue[top].x=i; queue[top].y=j; /////////////////////////////////////// queue[top].pre=minnumber; //上一步在队列中的位置 queue[top].value=VALUE(&queue[top]); //有多少位与目标不同 queue[top].flag=0; //标识位初始化 if(detect(&queue[top])) //检查是否为目标 {printlj(); //打印 find=1; //设找到标识位 break; } } } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值