隐式图的搜索问题

隐式图的搜索问题
问题描述:
3 х3九宫棋盘,放置数码为1~8的8个棋子,棋盘中留有一个空格,空格周围的棋子可以移动到空格中,从而改变棋盘的布局。

要求:根据给定初始布局和目标布局,如何移动棋子才能从初始布局到达目标布局,找到一种最少步骤的移动方法。

问题是否可解?
判断依据:将状态表示成一维的形式,求出除0(空格)之外所有数字的逆序数之和,也就是每个数字前面比它大的数字的个数的和,称为这个状态的逆序数。

若两个状态的逆序奇偶性相同,则可相互到达,否则不可相互到达。

易知本题S0到Sg状态有解。

题目思路
我们可以把每一种局面当作是一个1—8和‘.’的排列。空白格(‘.’)可以向四个方向移动,移动后的局面是新的排列。显然,我们可以通过bfs来搜索目标排列,并且一旦搜索到就一定是最少移动次数。我们需要解决的问题有两个。

1:如何记录每一种局面(排列)。

2:如何判断当前局面(排列)是否已经出现过,即之前的搜索过程中已经搜索过了,简单点说就是判重。

问题1解决方法:

我们可以直接用c++的string类型表示一个排列,并用一个结构体node表示’.‘位置的信息,包括当前排列,’.‘在排列中的位置,以及初始局面到该局面移动的步数。

String node:

struct node {

string pailie; //当前的排列

int pos; //’.'在排列中的位置

int step; //当前的步数

};

解决办法
这里用set集合来记录所有出现过的排列,即’.‘每移动一次,就将新排列存入set集合,用set的count方法可以返回一个元素在set集合中的个数,以此判断当前排列是否在之前出现过。

利用bfs求得目标步数。

#include<iostream>
#include<string> 
#include<string.h>
#include<queue>
#include<set>
using namespace std;
string a, b;	//a:是目标排列
               //b:是初始排列 
int p[4][2] = { 0,-1,-1,0,0,1,1,0 }; //上左下右 
set<string> s;
struct node {
string pailie;//当前的排列 
int pos;	//'.'在排列中的位置 
int step;	//当前的步数 
};
int bfs(int pos) {
queue<node>qu;
node cur, next;
cur.pailie = b;//初始化第一个节点,并且'.'的初始位置信息
cur.pos = pos;
cur.step = 0;
s.insert(b);	//将初始排列加入set的集合 
qu.push(cur);	// 将第一个节点入队 
int x, y, dx, dy, index1, index2;
while (!qu.empty()) {//队列不为空,就一直会循环 
cur = qu.front();	//取队头的元素 
qu.pop();	//头元素出队

index1 = cur.pos;
x = cur.pos / 3;		//转换成二维表的位置 
y = cur.pos % 3;
for (int i = 0; i < 4; i++) {//向四个方向搜索相邻位置 
dx = x + p[i][0];		//下一个位置的二维坐标 
dy = y + p[i][1];
index2 = dx * 3 + dy;	 //将二维坐标转换为一维坐标

if (dx >= 0 && dy >= 0 && dx < 3 && dy < 3) {
swap(cur.pailie[index1], cur.pailie[index2]);	//将'.'的位置与新位置交换 
if (s.count(cur.pailie) == 0) {//如果位置交换后的排列未在set集合中,即未搜索到这种情况 
next.pailie = cur.pailie;	//初始化新的节点 
next.pos = index2;
next.step = cur.step + 1;

if (next.pailie == a) return next.step;	//如果新排列与目标排列一致,返回步数
s.insert(next.pailie);	//将新的排列插入到set集合
qu.push(next);	//新的节点入队 
}
swap(cur.pailie[index1], cur.pailie[index2]);//位置交换回来,并且下次与另一个方向的相邻位置交换 
			}
		}
	}

return -1; 	//循环结束还未找到与目标排列相同的排列 
}
int main() {
cin >> a >> b;
if (a == b) {
cout << 0 << endl;
return 0;
	}
for (int i = 0; i < 9; i++) {
if (b[i] == '.') {	//找到'.'的初始位置 
cout << bfs(i) << endl;
break;
		}
	}
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值