HDU 1043 Eight 八数码问题(经典问题)

题意

经典问题,就不再进行解释了。

这里主要是给你一个状态,然后要你求其到达\(1,2,3,4,5,6,7,8,x\)的转移路径。

解题思路

这里有很多的解法,我这里是参考的学长给的题解,真的写的巨好啊!可惜不知道是哪位学长写的>︿<。

下面就是学长写的题解了,自己改动和增添了一点。


经典问题,解法有很多,我们先来计算一下八数码问题一共有多少种状态。八数码问题包含九个字符,这九个字符可以任意排列,也就是\(9! = 362880\)。所以八数码问题的状态只有\(36W\),并不是很多。

插播一个知识点 下面三种方法均需要使用(用来记录该状态是否达到过)。

知识点一:康托展开(具体见百度,此处不再进行赘述)

百度百科

我们可以用康托展开来把一个排列转换为一个自然数,这样,我们就可以方便的知道某一个状态是否达到过。


方法一 打表

我们可以从最终状态

\[\left[\begin{matrix}1 & 2 & 3\\4 & 5 & 6\\7 & 8 & X\\\end{matrix}\right]
\]

其它所有状态进行搜索,并记录路径。然后对于每个询问,直接输出答案即可(也就相当于先打表)。


插播第二个知识点 下面两种方法均需要使用(用来判断该局面是否有解)。

知识点二:\(n\)数码问题的有解性

奇数码游戏两个局面可达,当且仅当两个局面下网格中的数依次写成1行\(n * n - 1\)个元素的序列后(不考虑空格),逆序对个数的奇偶性相同。例如某一个局面写成\([5,2,8,1,3,4,6,7]\)。该结论的必要性很容易证明:空格左右移动时,写成的序列显然不变;空格向上(下)移动时,相当于某个数与它后(前)边的n-1个数交换了位置,因为n-1是偶数,所以逆序对数的变化也只能是偶数。该结论的充分性证明较为复杂,我们将不在此大篇幅讨论这样一个数学问题

上面的结论还可以扩展到\(n\)为偶数的情况,此时两个局面可达,当且仅当两个局面对应网格写成序列后,“逆序对数之差”和''两个局面下空格所在的行数之差”奇偶性相同。事实上,在\(n*m\)网格上(\(n,m\geq2\)也服从上述两个结论之一(根据列数奇偶性分情况讨论)

总而言之,\(n*m\)数码问题的有解性判定,可以转化为求逆序对来解决

补充:其实就是如下所述

n*m数码:

m是奇数,俩种状态逆序对奇偶性相同。

m是偶数,俩种状态“逆序对+下空格之间的行数之差”的奇偶性一致。


下面的两种方法一定要先对局面进行有解性的判定,只对有解的情况进行搜索。否则,如果某一个局面无解,则无论什么搜索方案,都会完全的搜索完整个状态空间,也就是会超时


方法二 双向BFS(这里题解中没有代码,准备后续补坑)

如果不进行打表的话,我们可以使用双向BFS来降低复杂度(因为搜索树节点数量是随着层数指数级增长的)。

从题目所给状态和最终状态交替进行双向BFS,当某一次取出队首节点,发现另一个分支已经访问过这个状态时,说明两个BFS相遇,递归进行输出答案。


方法三 A*启发式搜索

局面的估价函数:用每一个值的当前位置与最终位置的曼哈顿距离作为每一个值的代价,对所有的值的代价求和,就是当前局面的代码。

按照估价函数进行启发式搜索即可。


代码实现(方法三:A*算法)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct node{
int s[9];
int cur, n;
int f, g, h;
//注意,因为优先队列原本是从大到小的排列,所以我们需要重写小于号,这里的小于的功能实际是大于号的功能
//这样优先队列里面排序才是从小到大进行的排序。
//我的实现方式是用友元函数重载小于运算符
friend bool operator <(const node a, const node b)
{
return a.f==b.f ? a.g > b.g : a.f > b.f;
}
//下面是学长写的小于号运算符重载形式,使用的是成员函数运算符重载
//bool operator<(const node &a) const {
// return a.f == f ? a.g < g : a.f < f;
//}
};
const char dir[]="dulr";
const int nexts[][2]={{1, 0}, {-1, 0}, {0, -1}, {0, 1}};
const int maxn=363000;
int fac[]={1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; //0-9的阶乘
bool vis[maxn];
int pre[maxn];
char path[maxn];
int cantor(const int s[])
{
int sum=0;
for(int i=0; i<9; i++)
{
int cnt=0;
for(int j=i+1; j<9; j++)
if(s[j] < s[i])
cnt++;
sum+=cnt*fac[9-i-1];
}
return sum;
}
int getval(const int *s) //曼哈顿距离
{
int val=0;
int x, y, tx, ty;
for(int i=0; i<9; i++)
{
x=i/3; y=i%3;
tx=(s[i]-1)/3; ty=(s[i]-1)%3;
val+=abs(x-tx)+abs(y-ty);
}
return val;
} void printans(int n)
{
if(pre[n])
{
printans(pre[n]);
printf("%c", path[n]);
}
}
void AStar(node s)
{
memset(vis, false, sizeof(vis));
priority_queue<node> q;
q.push(s);
vis[s.n]=true;
pre[s.n]=0;
int ans=0;
while(!q.empty())
{
node now=q.top();
q.pop();
if(now.n==ans)
{
printans(ans);
printf("\n");
return ;
}
int x=now.cur/3, y=now.cur%3;
for(int p=0; p<4; p++)
{
int tx=nexts[p][0]+x, ty=nexts[p][1]+y;
if(tx<0 || ty<0 || tx>2 || ty>2) continue;
node tmp=now;
tmp.cur=tx*3+ty;
swap(tmp.s[now.cur], tmp.s[tmp.cur]);
tmp.n=cantor(tmp.s);
if(vis[tmp.n]) continue;
vis[tmp.n]=true;
pre[tmp.n]=now.n;
path[tmp.n]=dir[p];
tmp.g++;
tmp.h=getval(tmp.s);
tmp.f=tmp.g+tmp.h;
q.push(tmp);
}
}
}
int main()
{
char ch;
node s;
while(scanf(" %c", &ch) !=EOF)
{
if(ch=='x')
ch='9', s.cur=0;
s.s[0]=ch-'0';
for(int i=1; i<9; i++)
{
scanf(" %c", &ch);
if(ch=='x')
ch='9', s.cur=i;
s.s[i]=ch-'0';
}
s.n=cantor(s.s);
s.g=0;
s.h=getval(s.s);
s.f=s.g+s.h;
int cnt=0;
for(int i=0; i<8; i++)
{
if(s.s[i]!=9)
for(int j=i+1; j<9; j++)
if(s.s[j] < s.s[i])
cnt++;
}
if(cnt&1)
printf("unsolvable\n");
else AStar(s); }
return 0;
}

HDU 1043 Eight 八数码问题 A*算法(经典问题)的更多相关文章

  1. HUD 1043 Eight 八数码问题 A*算法 1667 The Rotation Game IDA*算法

    先是这周是搜索的题,网站:http://acm.hdu.edu.cn/webcontest/contest_show.php?cid=6041 主要内容是BFS,A*,IDA*,还有一道K短路的,.. ...

  2. Hdu 1043 Eight (八数码问题)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1043 题目描述: 3*3的格子,填有1到8,8个数字,还有一个x,x可以上下左右移动,问最终能否移动 ...

  3. hdu 1043 Eight (八数码问题)【BFS】+【康拓展开】

    <题目链接> 题目大意:给出一个3×3的矩阵(包含1-8数字和一个字母x),经过一些移动格子上的数后得到连续的1-8,最后一格是x,要求最小移动步数. 解题分析:本题用BFS来寻找路径,为 ...

  4. 八数码(IDA*算法)

    八数码 IDA*就是迭代加深和A*估价的结合 在迭代加深的过程中,用估计函数剪枝优化 并以比较优秀的顺序进行扩展,保证最早搜到最优解 需要空间比较小,有时跑得比A*还要快 #include<io ...

  5. HDU 1043 八数码 Eight A*算法

    Eight Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  6. 【双向广搜+逆序数优化】【HDU1043】【八数码】

    HDU上的八数码 数据强的一B 首先:双向广搜 先处理正向搜索,再处理反向搜索,直至中途相遇 visit 和 队列都是独立的. 可以用一个过程来完成这2个操作,减少代码量.(一般还要个深度数组) 优化 ...

  7. [cdoj1380] Xiper的奇妙历险(3) (八数码问题 bfs + 预处理)

    快要NOIP 2016 了,现在已经停课集训了.计划用10天来复习以前学习过的所有内容.首先就是搜索. 八数码是一道很经典的搜索题,普通的bfs就可求出.为了优化效率,我曾经用过康托展开来优化空间,甚 ...

  8. 八数码问题(一) 暴力BFS + STL

    八数码问题是一个经典的人工智能问题.具体问题不累述了. 思路:由于存在多组测试数据,可以考虑“打表法“.所谓打表法,即枚举所有的初始情况,记录其到达终点的路径.而在这个题目中,顺序打表会调用很多次BF ...

  9. hdu 1043 Eight 经典八数码问题

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 The 15-puzzle has been around for over 100 years ...

随机推荐

  1. 014:Django内置的URL转换器

    Django内置的URL转换器: 上节中我们说了URL中传参的情况,传递参数是通过 <> 尖括号来进行指定的.并且在传递参数的时候,可以指定这个参数的数据类型,比如文章的 id 都是 in ...

  2. 这里面ID为002和005的记录是重复的,在这里要把其中一条去掉,达到下面的效果:

    --去掉重复的记录 select ID,Code,ColorNum from (     SELECT      ROW_NUMBER() OVER(         PARTITION BY Cod ...

  3. C# 获得对象的命名空间 ?.

    A a = new A(); var t = a?.ToString(); //t = WebApplication1.Controllers.A //获得命名空间和类名 var t1 = (A)nu ...

  4. mini-batch

    我们在训练神经网络模型时,最常用的就是梯度下降,梯度下降有一下几种方式: 1.Batch gradient descent(BGD批梯度下降) 遍历全部数据集算一次损失函数,然后算函数对各个参数的梯度 ...

  5. 随堂小测APP使用体验

    随堂小测APP使用体验 先要去注册账号需要填写用户名.密码.手机号.学号/教师号.学校.专业.即可注册,注册成功后,即可登录APP进,登陆进去以后.会有两个界面,课堂和我的,注册.登录简单,通俗易懂, ...

  6. 【转载】opencv 二值化函数——cv2.threshold

    https://blog.csdn.net/weixin_38570251/article/details/82079080 threshold:固定阈值二值化, ret, dst = cv2.thr ...

  7. springboot(四).配置FastJson自定义消息转化器

    配置FastJson自定义消息转化器 一.fastJson简介 fastJson是阿里巴巴旗下的一个开源项目之一,顾名思义它专门用来做快速操作Json的序列化与反序列化的组件.它是目前json解析最快 ...

  8. 02 body标签中的相关标签

    今日内容: 字体标签: h1~h6.<font>.<u>.<b>.<strong><em>.<sup>.<sub> ...

  9. Linux高级调试与优化——信号量机制与应用程序崩溃

    背景介绍 Linux分为内核态和用户态,用户态通过系统调用(syscall)进入内核态执行. 用户空间的glibc库将Linux内核系统调用封装成GNU C Library库文件(兼容ANSI &am ...

  10. Linux驱动开发8——中断处理

    中断包括软中断和硬中断两种,中断是一种异步I/O机制,即中断可以发生在任意时间点. 1.硬中断 硬件中断包括触发中断和处理中断两部分,而维系两者的是中断号,中断号是一种硬件资源. 1.1.注册和释放中 ...