poj 1077 Eight

题目:

是一道经典的八数码问题。将一个3×3的矩阵,通过每次和四周的数字进行互换,最终达到目标状态。其中用字母“l”表示相左移动,“u”表示向上,“r”表示向右,“d”表示向下,最后结果,输出移动的字母。

样例:

目标位置是                                                             初始位置是:

                                 

 为了从初始位置到达目标位置,需要先将x与5换位置,再与6换位置,也就是先往右移动再往下移动,故输出的值为  rd

 如果谜题没有解决方案,您将打印到标准输出,或者单词“unsolvable”,或者一个完全由字母“r”、“l”、“u”和“d”组成的字符串,描述一系列产生解决方案的动作。该字符串不应包含空格并从行首开始。

 思路:

首先可以看出是一道BFS(深度优先搜索的提目),我们用暴力法从x位置出发分别从左、上、右、下四个位置来移动,直到与目标状态相同。

思路虽然简单,但是仔细发现是会有重复的状态出现的,例如样例假如x向右移动后,下一次移动又向左移动了,就会发现回到初始位置了。如果不去掉这些重复的状态,程序会产生很无效的操作,复杂度大大增加。例如,总共的状态有9!=362880,再与新状态相对比,最多可产生9!×9!次检查。因此该问题最重要的是判重。

本题可以用数学方法“康托展开”来判重。通过康托展开将每个状态按照字典序排序并得到该状态的序号,例如012345678的序号就是1。通过其计算公式就可以实现判重的作用(具体原理和计算公式可以百度)

其中BFS用STL中的queue来实现。

扫描二维码关注公众号,回复: 13465123 查看本文章

 

代码编写:

为了便于运算我们在进行搜索时将题中x转换成了0,故在输入的时候需要将我们输入的字符数组转换成int型,如下代码段。这里还有一个细节需要注意的是,这里的输入用的cin.getline()函数而不是简单的cin>>形式输入,因为cin>>形式输入会自动过滤掉不可见字符(如空格 回车 tab等)这样的话将字符数组转化成int型就会出错,因为题目的输入是有空格的,所以这里要用cin.getline()函数(cin.getline()函数可以接收空格等)

int main()
{
	char s[100];
	cin.getline(s, 100);//输入
	int pos = 0;
	for (int i = 0; s[i] != '\0'; i++)//将char型转换为int型
	{
		if (s[i] == ' ') continue;
		else if (s[i] == 'x') start[pos++] = 0;//将x转为0
		else start[pos++] = s[i] - '0';
	}
	int num = bfs();
	if (num == -1) printf("unsolvable\n");
	return 0;
}

 康托展开的代码:

long int factory[] = { 1,1,2,6,24,120,720,5040,40320,362880 };//康托展开用到的常数
bool Cantor(int str[], int n)//用康托展开判重
{
	long result = 0;
	for (int i = 0; i < n; i++)
	{
		int counted = 0;
		for (int j = i + 1; j < n; j++)
		{
			if (str[i] > str[j])
				++counted;
		}
		result += counted * factory[n - i - 1];
	}
	if (!visited[result])
	{
		visited[result] = 1;
		return 1;
	}
	else return 0;
}

AC代码如下:

(需要用G++来运行,c++会出现编译错误,因为此题有多种解法,运用BFS+康托展开并不是最好的方法,但由于自己的水平不够,目前也只能用这种较为简单易懂的方法)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 362880;//状态共9!=362880
struct node
{
	int state[9];//记录一个八数码的排列,即一个状态
	int dis;//记录到起点的距离
	string ans;
};
int dir[4][2] = { {-1,0},{0,-1},{1,0},{0,1} };//左、上、右、下顺时针方向,用于遍历
char turn[4] = { 'l','u','r','d' };//用字母表示左、上、右、下
int visited[maxn] = { 0 };//标记已走过的位置
int start[9];//起点
int goal[9] = { 1,2,3,4,5,6,7,8,0 };//目标

long int factory[] = { 1,1,2,6,24,120,720,5040,40320,362880 };//康托展开用到的常数
bool Cantor(int str[], int n)//用康托展开判重
{
	long result = 0;
	for (int i = 0; i < n; i++)
	{
		int counted = 0;
		for (int j = i + 1; j < n; j++)
		{
			if (str[i] > str[j])
				++counted;
		}
		result += counted * factory[n - i - 1];
	}
	if (!visited[result])
	{
		visited[result] = 1;
		return 1;
	}
	else return 0;
}

bool check(int x, int y)//判断x,y是否越界
{
	if (x >= 0 && x < 3 && y >= 0 && y < 3)
		return true;
	else return false;
}

int bfs()//深度优先搜索
{
	node head;
	memcpy(head.state, start, sizeof(head.state));//复制起点的状态
	head.dis = 0;
	queue<node>q;//用队列记入状态
	Cantor(head.state, 9);//用康托展开判重,目的是对起点的visited[]赋初值
	q.push(head);//将起点状态进队
	while (!q.empty())//处理队列
	{
		head = q.front();
		q.pop();
		int z;
		if (memcmp(head.state, goal, sizeof(goal)) == 0)//与目标状态对比
		{
			cout << head.ans << endl;//打印出题目的结果
			return head.dis;
		}
		for (z = 0; z < 9; z++)//找到状态元素为0的位置,即为x的位置
		{
			if (head.state[z] == 0)
				break;
		}
		int x = z % 3;//横坐标
		int y = z / 3;//纵坐标
		for (int i = 0; i < 4; i++)//遍历上、下、左、右四个位置
		{
			int newx = x + dir[i][0];
			int newy = y + dir[i][1];
			int nz = newx + 3 * newy;//转化为一维
			if (check(newx, newy))//如果每越界
			{
				node newnode = head;//复制head的状态,用来存新位置
				swap(newnode.state[z], newnode.state[nz]); //0的交换
				newnode.dis++;
				if (Cantor(newnode.state, 9))//用康托展开判重
				{
					newnode.ans = head.ans + turn[i];//记录移动的位置,用字母表示
					q.push(newnode);//将新的状态存入队列
				}
			}
		}
	}
	return -1;
}

int main()
{
	char s[100];
	cin.getline(s, 100);//输入
	int pos = 0;
	for (int i = 0; s[i] != '\0'; i++)//将char型转换为int型
	{
		if (s[i] == ' ') continue;
		else if (s[i] == 'x') start[pos++] = 0;//将x转为0
		else start[pos++] = s[i] - '0';
	}
	int num = bfs();
	if (num == -1) printf("unsolvable\n");
	return 0;
}

 大数据201 liyang

猜你喜欢

转载自blog.csdn.net/zjsru_Beginner/article/details/121046241