[Aizu] ALDS1_13_B: 8 Puzzle

问题

传送门:8 Puzzle

概述

8拼图问题的目标是在3×3单元格上完成碎片,其中一个单元格是空的空间。
在这个问题中,空的空间用0表示,而碎片用1到8的整数表示,如下所示

1 3 0
4 2 5
7 8 6

您在一个步骤里可以将一块碎片移向空白区域。 您的目标是在最短的时间(最少的步骤)内到达以下的状态

1 2 3
4 5 6
7 8 9

编写一个程序,读取拼图的初始状态并输出解决拼图的最少步骤。

输入

给出了表示碎片或空间的3×3整数

输出

在一行中输出最少的步骤。

约束条件

问题一定有解

样例输入

1 3 0
4 2 5
7 8 6

样例输出

4

求解

分析

在这里插入图片描述由于没有看过关于这种题的算法知识, 只好使用最简单的方法, 往出试
由于一个游戏状态对于空格最多只有4种操作, 上下左右, 每一次操作都会形成新的游戏状态, 由于是求最小的步骤, 所以是以广度优先的方法处理状态, 如果在处理新的状态的时候, 发现该状态之前出现过, 那么就无需处理, 直接处理下一个状态
如果发现某一种状态刚好是所求状态, 那么直接结束

设计

保存游戏的状态需要一个结构体 Board, 存储了当前游戏状态, 空格所在的位置, 添加了上下左右移动空格的操作, 以及判断是否到达最终结果的操作.
为了判断状态是否重复出现, 使用set集合, 它存在于STL中, 是用二叉搜索树实现的(也许更高级, 但至少具有二叉搜索树的性质), 不会允许插入重复的元素, 为了使用这个集合, 需要重载小于运算符.
为了广度处理状态, 使用queue队列, 同样存在于STL中, 同时使用了pair, 为了保证数据的正确性, 重载了赋值运算符(不过最后发现似乎不用重载也可以, 是以数组名定义的, 而不是用指针)

编码

// File name: 8 Puzzle
// Written by: by_sknight
// Date: 2019/5/21
#include <bits/stdc++.h>
using namespace std;
#define size 3


struct Board {
	// 3 * 3 的存储区
	int P[size][size];
	// 空格所在的位置
	int space_x, space_y;
	bool up() {
		if (space_y == 0) {
			return false;
		}
		swap(P[space_x][space_y], P[space_x][space_y - 1]);
		space_y--;
		return true;
	}
	bool down() {
		if (space_y == size - 1) {
			return false;
		}
		swap(P[space_x][space_y], P[space_x][space_y + 1]);
		space_y++;
		return true;
	}
	bool left() {
		if (space_x == 0) {
			return false;
		}
		swap(P[space_x][space_y], P[space_x - 1][space_y]);
		space_x--;
		return true;
	}
	bool right() {
		if (space_x == size - 1) {
			return false;
		}
		swap(P[space_x][space_y], P[space_x + 1][space_y]);
		space_x++;
		return true;
	}
	// 判断是否结束
	bool isOver() {
		int i, j, v;
		v = 1;
		for (i = 0; i < size; i++) {
			for (j = 0; j < size; j++) {
				if (i == size - 1 && j == size - 1) {
					return true;
				}
				if (P[i][j] != v)
					return false;
				v++;
			}
		}
		return true;
	}
	// 重载赋值运算符, 之后会用到赋值
	void operator=(const Board& obj) {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				P[i][j] = obj.P[i][j];
			}
		}
		space_x = obj.space_x;
		space_y = obj.space_y;
	}
	// 重载小于运算符, set集合的判断会用到
	bool operator<(const Board& obj) const{
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				if (P[i][j] < obj.P[i][j])
					return true;
				else if (P[i][j] > obj.P[i][j])
					return false;
			}
		}
		return false;
	}
};

set<Board> status;
Board bo;

void solution() {
	queue<pair<Board, int> > Q;
	Board now_board;
	int now_step;
	Q.push(make_pair(bo, 0));
	while (1) {
		now_board = Q.front().first;
		now_step = Q.front().second;
		Q.pop();
		// 如果满足最终条件, 退出循环
		if (now_board.isOver()) {
			break;
		}
		// 如果当前棋盘状态已经出现过, 则直接跳过, 处理下一个
		if (status.insert(now_board).second == false) { 
			continue;
		}
		// 如果可以向上移动空格, 则将向上移动之后的状态与步数存入队列
		if (now_board.up()) {
			Q.push(make_pair(now_board, now_step + 1));
			// 之后恢复之前的状态
			now_board.down();
		}
		// 以下三个同上
		if (now_board.down()) {
			Q.push(make_pair(now_board, now_step + 1));
			now_board.up();
		}
		if (now_board.left()) {
			Q.push(make_pair(now_board, now_step + 1));
			now_board.right();
		}
		if (now_board.right()) {
			Q.push(make_pair(now_board, now_step + 1));
			now_board.left();
		}
	}
	while (!Q.empty()) {
		Q.pop();
	}
	cout << now_step << endl;
	return;
}

int main(void) {
	for (int i = 0; i < size; i++) {
		for (int j = 0; j < size; j++) {
			cin >> bo.P[i][j];
			if (bo.P[i][j] == 0) {
				bo.space_x = i;
				bo.space_y = j;
			}
		}
	}
	solution();
}

结果

在这里插入图片描述
在这里插入图片描述

总结

关于运算符的重载, 需要确定什么情况下使用, 而不是盲目的直接使用
需要去看一下解决这种问题的更灵活的解题思路

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值