hiho_100_八数码
题目大意
8数码问题,在3x3的矩阵中填入0-8九个数字,0可以和它相邻的数字进行交换。从初始状态到达状态F(3x3的方格从上到下,从左到右,形成的数字串为123456780)所需要最少移动的次数。
题目分析
将3x3矩阵中的当前情形记为一个状态,用9个字符表示。然后根据方格0和它相邻的方格交换来进行状态的转移。可以采用广度优先的方式来进行搜索遍历,直到到达状态F,或者将所有的可能情况都遍历过来。
那么,如何判断一个状态是否被遍历过了呢?由于 用9个字符表示状态,每个字符有0-8 共9种可能,因此总共的情形为 9^9 = 387420489,空间复杂度太高,因此需要压缩: 由于9个方格中的数字均不相同,那么通过移动方格能够达到的合理的状态数就只有 9! =362880. 复杂度就可以接受了。因此需要实现将 0-8的一个排列定位到它在0-8全排列中的位置。
通过康托展开来实现:
康托展开法, 长度为n的排列 num[1,2....n],其在n的全排列中的序号为
X = a[1](n-1)! + a[2](n-2)! +... +a[n]*0!;
其中a[i] 表示在num[i+1, ...n]中比num[i]小的数量
将空间进行压缩之后,可以用广度优先搜索来解决,但是也可以通过A-star 算法来进行优化:
对每个状态都维护一个 F = G + H,表示该状态的代价。 G表示从初始状态到达该状态所花费的代价,H表示从该状态到达终点状态的估计代价。要求估计代价 H 一定小于等于 从该状态到达终点状态的实际代价!
将当前可进入的状态按照F进行排序,选择F最小的那个进行扩展。
H 可以通过估计函数来获得,本题中的估计函数就可以设置为 当前3x3矩阵中方格中的数字和最终状态对应方格中数字不同的方格的总数目。很容易证明 H 一定小于等于 实际从该状态到达终点状态的代价。
实现
#include<iostream>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
char init_digit[3][3];
bool visited[362890];
struct Node{
char digit[3][3]; //状态记录
int g; //从起始状态到达该状态,已经花费的代价
int f; //该状态的价值, f = g + h(估价函数)
};
struct Cmp{
bool operator()(const Node& node1, const Node& node2){
return node1.f > node2.f;
}
};
void Init(){
memset(visited, false, sizeof(visited));
}
//康托展开法, 长度为n的排列 num[1,2....n],其在n的全排列中的序号为
//X = a[1]*(n-1)! + a[2]*(n-2)! +... +a[n]*0!;
//其中a[i] 表示在num[i+1, ...n]中比num[i]小的数量 /*逆康托展开
X = a[1]*(n-1)! + a[2]*(n-2)! + ... + a[n]*0!, a[i] 表示 num[i+1, ... n]中小于a[i]的数目
已知X,求出对应的 num[1,2...n]
可知, a[i]肯定 <= n - i. 则 a[i]*(n-i)! <= (n-i)*(n-i)!, 那么 a[i+1]*(n-i-1)! + a[i+2]*(n-i-2)! + ... a[n]*0!
<= (n-i-1)*(n - i - 1)! + (n-i-2)*(n - i - 2)! + .... 1*1! + 0*0! 由数学归纳法可以知道, 1*1! < 2!, 1*1! + 2*2! < 3!, .... 因此
(n-i-1)*(n - i - 1)! + (n-i-2)*(n - i - 2)! + .... 1*1! + 0*0! < (n-i)!
a[i+1]*(n-i-1)! + a[i+2]*(n-i-2)! + ... a[n]*0! < (n-i)! 因此, X / (n-1)! 的商c 即为 a[1], X / (n-1)! 的余数 r / (n-2)! 的商即为 a[2]....
*/ int digit2Int(char arr[3][3]){
// arr[0][0], arr[0][1] ... arr[2][2] 记为 num[1], num[2] ... num[9]
int result = 0, t = 1;
for (int i = 8; i >= 0; i--){
int row = i / 3, col = i % 3;
int k = 0;
for (int j = i + 1; j < 9; j ++)
k += (arr[j / 3][j % 3] < arr[row][col]);
result += k*t;
t *= (9 - i);
}
return result;
} int Estimate(char arr[3][3]){
int sum = 0;
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
if (3 * i + j == 8)
continue;
if (arr[i][j] != 3 * i + j + '1')
sum++;
}
}
return sum;
}
bool Succeed(char arr[3][3]){
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
int target = (3 * i + j + 1) % 9;
if (arr[i][j] != target + '0')
return false;
}
}
return true;
}
int move_step[4][2] = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } };
int Search(){
priority_queue<Node, vector<Node>, Cmp> pq;
//queue<Node> pq;
int X = digit2Int(init_digit);
visited[X] = true;
Node node; for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
node.digit[i][j] = init_digit[i][j];
}
}
node.g = 0;
node.f = 0 + Estimate(node.digit);
pq.push(node);
while (!pq.empty()){
//node = pq.front();
node = pq.top();
pq.pop();
if (Succeed(node.digit))
return node.g;
char cur_digit[3][3];
int cur_g = node.g;
int zero_r = 0, zero_c = 0;
for (int i = 0; i < 3; i++){
for (int j = 0; j < 3; j++){
cur_digit[i][j] = node.digit[i][j];
if (cur_digit[i][j] == '0'){
zero_r = i;
zero_c = j;
}
}
}
for (int i = 0; i < 4; i++){
int next_r = zero_r + move_step[i][0];
int next_c = zero_c + move_step[i][1];
if (next_r >= 0 && next_r <= 2 && next_c >= 0 && next_c <= 2){
memcpy(node.digit, cur_digit, sizeof(cur_digit));
node.digit[next_r][next_c] = '0';
node.digit[zero_r][zero_c] = cur_digit[next_r][next_c];
int X = digit2Int(node.digit);
if (visited[X])
continue;
visited[X] = true;
node.g = cur_g + 1;
node.f = node.g + Estimate(node.digit);
pq.push(node);
}
}
}
return -1;
}
int main(){
int T;
scanf("%d", &T);
while (T--){
for (int i = 0; i < 3; i++){
getchar();
scanf("%c %c %c", &init_digit[i][0], &init_digit[i][1], &init_digit[i][2]);
}
Init();
int step = Search();
if (step == -1)
printf("No Solution!\n");
else
printf("%d\n", step);
}
return 0;
}
hiho_100_八数码的更多相关文章
- A*算法 -- 八数码问题和传教士过河问题的代码实现
前段时间人工智能的课介绍到A*算法,于是便去了解了一下,然后试着用这个算法去解决经典的八数码问题,一开始写用了挺久时间的,后来试着把算法的框架抽离出来,编写成一个通用的算法模板,这样子如果以后需要用到 ...
- 八数码问题:C++广度搜索实现
毕竟新手上路23333,有谬误还请指正. 课程设计遇到八数码问题(这也是一坨),也查过一些资料并不喜欢用类函数写感觉这样规模小些的问题没有必要,一开始用深度搜索却发现深搜会陷入无底洞,如果设定了深度限 ...
- ACM/ICPC 之 BFS-广搜进阶-八数码(经典)(POJ1077+HDU1043)
八数码问题也称为九宫问题.(本想查查历史,结果发现居然没有词条= =,所谓的历史也就不了了之了) 在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同.棋盘上还有一个 ...
- BFS(八数码) POJ 1077 || HDOJ 1043 Eight
题目传送门1 2 题意:从无序到有序移动的方案,即最后成1 2 3 4 5 6 7 8 0 分析:八数码经典问题.POJ是一次,HDOJ是多次.因为康托展开还不会,也写不了什么,HDOJ需要从最后的状 ...
- 双向广搜+hash+康托展开 codevs 1225 八数码难题
codevs 1225 八数码难题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description Yours和zero在研究A*启 ...
- UVALive 6665 Dragonâs Cruller --BFS,类八数码问题
题意大概就是八数码问题,只不过把空格的移动方式改变了:空格能够向前或向后移动一格或三格(循环的). 分析:其实跟八数码问题差不多,用康托展开记录状态,bfs即可. 代码: #include <i ...
- P1379 八数码问题
aoapc上的八数码问题,在luogu上也有类似的题,p1379,经典题目,lrj给出了一个算法,同时给出了三种判重的方法.本来想用std::queue改写一下,但是出了各种问题,只好抄代码ac掉这道 ...
- [cdoj1380] Xiper的奇妙历险(3) (八数码问题 bfs + 预处理)
快要NOIP 2016 了,现在已经停课集训了.计划用10天来复习以前学习过的所有内容.首先就是搜索. 八数码是一道很经典的搜索题,普通的bfs就可求出.为了优化效率,我曾经用过康托展开来优化空间,甚 ...
- hdu 1043 Eight 经典八数码问题
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 The 15-puzzle has been around for over 100 years ...
随机推荐
- OracleHelper
/// <summary> /// OracleServer数据库访问的通用工具类 /// </summary> public abstract class OracleHel ...
- Python学习笔记-Day2-Python基础之字符串操作
字符串的常用操作包括但不限于以下操作: 字符串的替换.删除.截取.复制.连接.比较.查找.分割等 这里将对字符串的内置操作方法进行总结归纳,重点是以示例的方式进行展示. 使用type获取创建对象的类 ...
- Kernel启动时 驱动是如何加载的module_init,加载的次序如何;略见本文
Init.h中有相关initcall的启动次序,在system.map中可看出具体的__initcall指针的前后次序 #define pure_initcall(fn) __define_initc ...
- ie调试工具
调试ie6真是疯掉了,要到一个包,类firbug,js写的不错. 链接:http://pan.baidu.com/s/1c2JOs0K 密码:fea8
- ACM-ICPC国际大学生程序设计竞赛北京赛区(2016)网络赛 A Simple Job
描述 Institute of Computational Linguistics (ICL), Peking University is an interdisciplinary institute ...
- Android超类
android.test.AndroidTestCase android写测试类的超类 android.database.sqlite.SQLiteOpenHelper sqllite的超类
- zookeeper系列之六—zookeeper之应用
http://www.cnblogs.com/sharpxiajun/archive/2013/06/02/3113923.html Zookeeper是hadoop的一个子项目,虽然源自hadoop ...
- C语言中的const
今天探讨const,首先来说是将变量常量化.为什么要将变量常量化,原因有诸多好处有诸多.比如可以使数据更加安全不会被修改! 但是这个词有几个点要注意,那就是他究竟修饰了谁? 1.const int a ...
- 如何快速清除.svn文件
Windows Registry Editor Version 5.00[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Folder\shell\清除SVN信息] @=&qu ...
- C#闪屏
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...