九宫重排问题的实现

九宫重排问题的实现

把1—8个数字按要求推动得出结果。如把把8 5 7 3 6 4 1 2推成1 2 3 4 5 6 7 8。就如同对于密码锁的破解,8个数字有着无穷个不同变幻的排列方式,游戏攻略如同玩滑动拼图,这里锻炼人的逆向思维能力,为进一步的九宫格游戏做准备。游戏中必须注意的是,以最少的移动次数拼出结果者为胜。
本文采用【蓝桥杯】历届试题中的九宫重排问题来讲述此算法:
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入格式
  输入第一行包含九宫的初态,第二行包含九宫的终态。
输出格式
  输出最少的步数,如果不存在方案,则输出-1。
样例输入
12345678.
123.46758
样例输出
3
样例输入
13524678.
46758123.
样例输出
22

// A*算法
#include<iostream>
#include<vector>
#include<time.h>
#include<algorithm>
using namespace std;

const int GRID = 3;         //Grid表示表格的行数(列数),这是3*3的九宫格
int rightPos[9] = { 4, 0, 1, 2, 5, 8, 7, 6, 3 };
//目标状态时,若p[i][j]=OMG,那么3*i+j = rightPos[OMG]

struct state {
	int panel[GRID][GRID];
	int level;      //记录深度
	int h;
	state* parent;

	state(int level) :level(level) {}

	bool operator == (state& q) {
		//判断两个状态是否完全相等(对应位置元素相等),完全相等返回true,否则返回false
		for (int i = 0; i < GRID; i++) {
			for (int j = 0; j < GRID; j++) {
				if (panel[i][j] != q.panel[i][j])
					return false;
			}
		}
		return true;
	}

	state& operator = (state& p) { //以状态p为当前状态赋值,对应位置元素相同
		for (int i = 0; i < GRID; i++) {
			for (int j = 0; j < GRID; j++) {
				panel[i][j] = p.panel[i][j];
			}
		}
		return *this;
	}
};

void dump_panel(state* p) {         //将八数码按3*3矩阵形式输出
	for (int i = 0; i < GRID; i++) {
		for (int j = 0; j < GRID; j++)
			cout << p->panel[i][j] << " ";
		cout << endl;
	}
}

int countH(state& st) {             //给定状态st,计算它的h值。
	int h = 0;
	for (int i = 0; i < GRID; i++) {
		for (int j = 0; j < GRID; j++) {
			if (st.panel[i][j] != 0)
				h += abs(rightPos[st.panel[i][j]] / GRID - i) +
				abs(rightPos[st.panel[i][j]] % GRID - j);
			//h=各个将牌与其目标位置的距离之和.距离定义为:行下标之差的绝对值+列下标之差的绝对值。
		}
	}
	return h;
}

int findZero(state& st) {               //找到零值元素,返回其在表中的位置
	for (int i = 0; i < GRID; i++) {
		for (int j = 0; j < GRID; j++) {
			if (st.panel[i][j] == 0)
				return i * 3 + j;
		}
	}
}

int f(state* p) {               //计算并返回f()值,即h值+level
	return countH(*p) + p->level;
}

bool compare_state(state* p, state* q) {       //比较两个状态的f值
	return f(p) > f(q);
}

vector<state*> open_table;         //open表
vector<state*> close_table;        //close表

vector<state*>::iterator look_up_dup(vector<state*>& vec, state* p) {
	vector<state*>::iterator it_r = vec.begin();
	for (; it_r < vec.end(); it_r++) {
		if ((*(*it_r)) == *p) {
			break;
		}
	}
	return it_r;
}
state* search(state& start) {      //A*算法进行搜索
	int level = 0;
	open_table.push_back(&start);
	int count = 0;

	while (!open_table.empty()) {
		sort(open_table.begin(), open_table.end(), compare_state);
		//对open表中的元素进行排序

		state* p = open_table.back();
		open_table.pop_back();

		if (countH(*p) == 0)
			return p;           //所有将牌到达目标位置,搜索过程结束
		level = p->level + 1;

		int zeroPos = findZero(*p);
		int x = zeroPos / 3;        //空格的行下标
		int y = zeroPos % 3;        //空格的列下标
		for (int i = 0; i < 4; i++) {  //上下左右四个方向
			int x_offset = 0, y_offset = 0;
			switch (i) {
			case 0:x_offset = 0, y_offset = 1; break;   //右
			case 1:x_offset = 0, y_offset = -1; break;//左
			case 2:x_offset = 1, y_offset = 0; break;//上
			case 3:x_offset = -1, y_offset = 0; break;//下
			};

			if (x + x_offset < 0 || x + x_offset >= GRID || y + y_offset < 0 || y + y_offset >= GRID) {
				continue;
				//若移动一步后,将超出上/下/左/右边界,则这个方向不可走,尝试下一个方向
			}
			state* q = new state(level);       //这个方向可走,扩展下一个节点
			q->parent = p;
			*q = *p;
			q->panel[x][y] = q->panel[x + x_offset][y + y_offset];
			q->panel[x + x_offset][y + y_offset] = 0;//空格沿这个方向移一步

			bool skip = false;
			vector<state*>::iterator dup = look_up_dup(open_table, q);
			//若q已在open表中,则对open表中的信息进行更新
			if (dup != open_table.end()) {
				if (f(q) < f(*dup)) {
					(*dup)->level = q->level;
					(*dup)->parent = q->parent;
				}
				skip = true;
			}

			dup = look_up_dup(close_table, q);
			if (dup != close_table.end()) {  //若q已在close表中,且f值比原值小,
				if (f(q) < f(*dup)) {            //则将q从close表清除,加入open表
					delete* dup;
					close_table.erase(dup);
					open_table.push_back(q);
					skip = true;
				}
			}

			if (!skip) {
				open_table.push_back(q);
			}
		}

		close_table.push_back(p);
	}
}

void dump_solution(state* q)       //输出解路径
{
	vector<state*> trace;
	while (q) {
		trace.push_back(q);
		q = q->parent;
	}

	int count = 0;

	while (!trace.empty()) {
		cout << "Step " << count << endl;
		dump_panel(trace.back());
		cout << "h: " << countH(*trace.back()) << "\tg:" << count << "\tf: " << f(trace.back()) << endl;
		cout << "------------------------------\n\n";
		trace.pop_back();
		count++;
	}
}
int main()
{
	state p(0);
	state* q;

	p.panel[0][0] = 2;//设置初始状态
	p.panel[0][1] = 8;
	p.panel[0][2] = 3;
	p.panel[1][0] = 1;
	p.panel[1][1] = 6;
	p.panel[1][2] = 4;
	p.panel[2][0] = 7;
	p.panel[2][1] = 0;
	p.panel[2][2] = 5;

	p.parent = NULL;
	q = search(p);
	dump_solution(q);
	system("pause");
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值