poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)
题目来源:
http://poj.org/problem?id=1077
题目大意:
给你一个由1到8和x组成的3*3矩阵,x每次可以上下左右四个方向交换。求一条路径,得到12345678x这样的矩阵。若没有路径,则输出unsolvable。
经典的八数码问题。
这题我用A*算法做的。推荐一篇博客,从大体上介绍了一下启发式算法的代表A*算法:
https://www.cnblogs.com/zhoug2020/p/3468167.html
首先就是判重的问题,搜索的状态是九个数(含x),开个九重数组也不是不可以,但用cantor展开hash一下还是方便的。所谓cantor展开,就是对1..n的所有排列,唯一对应一个1..n!的值。
cantor展开解决互不相同的元素的全排列问题(互不相同就可以了)。代码中的cantor展开,可以当做模板用。
康托展开的公式是 X=a1*(n-1)!+a2*(n-2)!+...+ai*(i-1)!+...+an-1*1!+an*0! 其中,ai为当前元素比之后多少个元素大。
其实理解公式很简单,给你排列2,4,1,5,3,你可以这么想,第一个放2,说明1开头的都排完了,至少比4!种排列靠后了,第二个数是4,说明2,1开头和2,3开头的都排过了,又比2*3!种排列靠后了。以此类推。。
其次就是奇偶剪枝了。奇偶剪枝是一个常见的预判断的方法。接下来详细阐述一下:
1、逆序数的概念
给定数列a[1..n],记LESS(i)为a[i+1]到a[n]中比a[i]小的数的个数,则逆序数为∑LESS(i)。比如有数列a[1..5]=[3,7,2,19,2],则LESS(1)到LESS(5)分别为2,2,0,1,0,所以逆序数为2+2+0+1+0=5。
2、树状数组(线段树)求逆序数
将值val与序号id放在结构体中一起排序,先按值的升序排列,值相同时,再按序号升序排列(这样后面求LESS值的时候不会把相同的数算进去)。
比如a[1..5]=[3,7,2,19,2],排序成[2:3,2:5,3:1,7:2,19:4](冒号后为排序用结构体里的id)。
然后维护[1..n]的树状数组。初始全为0,顺序遍历排好序的结构体,将id处的值变为1,并查询i+1到n的和,即可得到LESS值,再将LESS值求和,即可。
比如上例顺序执行可得LESS(3)=0,LESS(5)=0,LESS(1)=2,LESS(2)=2,LESS(4)=1。逆序数为5。
3、八数码的奇偶剪枝
对于1..n的排列,交换相邻的元素,逆序数奇偶性一定改变。所以如果将八数码中的x视作9的话,八数码可以看成是1..9的排列。左右交换,是一次相邻元素的交换,上下交换,可以看成3+2=5次相邻交换,逆序数奇偶性一定改变。
现在我们改变一下1中对LESS(i)的定义,重定义为对于数i,LESS(i)为从它的位置到最后比它小的数的个数。由于现在的排列没有相同的数,所以求和得到的逆序数是不变的。
如果从1..9的排列中除去9,可以发现1..8的LESS值不变,而上下/左右交换前后LESS(9)的奇偶性一定改变,又结合之前交换前后逆序数奇偶性改变,得到1..8逆序数奇偶性不变。
还有一个15迷的问题,奇偶剪枝也是一样的分析思路,只是加了个辅助量。
最后就是Astar算法本身了:
首先需要一个优先队列。由于对优先级的需要,我们需要在结构体中对<运算符进行重载(详见代码)。
然后结构体中存储路径最好还是用string吧,毕竟vector还是没它方便。
好像就是这么多。搜索这种东西,复杂度比较难把握,所以能优化的地方尽量优化一下吧。比如搜索方向,感觉还是先搜索down和right比较好,虽然可能效果不太明显。
这道题还要继续做。hdu1043还没A掉,双向BFS,还有什么打表方法还没有尝试写呢。加上!!!,表示要记得做!
#include<iostream>
#include<cstdio>
#include<string>
#include<queue>
#include<cstring> using namespace std; char buf[];
int puz[]; int fac[]={,,,,,,,,,};
int cantor(int s[])
{
int sum=;
for(int i=;i<;i++)
{
int num=;
for(int j=i+;j<;j++)
if(s[j]<s[i])
num++;
sum+=(num*fac[-i-]);
}
return sum+;
} struct tnode
{
int puz[];
string path;
int loc;//0的位置
int status;//cantor展开值
int n;//搜索深度
int f;//估值函数
int getloc()
{
for(int i=;i<;i++)
if(puz[i]==)
return i;
}
bool operator<(const tnode& y) const
{
return f>y.f;
}
};
int vis[];
int dis[]={,,,,,,,,};
int a[]={,,-,-};
char b[]="rdlu"; int aim=; int main()
{
while(scanf("%[^\n]",buf)!=EOF)
{
getchar(); for(int i=,cnt=;buf[i]!='\0';i++)
{
if(buf[i]=='x')
puz[cnt++]=;//用0存储x
else if(buf[i]>=''&&buf[i]<='')
puz[cnt++]=buf[i]-'';
}
//for(int i=0;i<9;i++) printf("%d ",puz[i]); printf("\n");
//printf("%d\n",cantor(puz)); int cnt=;
for(int i=;i<;i++)
if(puz[i]!=)
{
for(int j=i+;j<;j++)
if(puz[j]!=&&puz[j]<puz[i])
cnt++;
}
if(cnt%==)
{
printf("unsolvable\n");
continue;
} priority_queue<tnode> q;
memset(vis,,sizeof(vis));
tnode node1;
memcpy(node1.puz,puz,sizeof(puz));
node1.path="";
node1.loc=node1.getloc();
node1.status=cantor(node1.puz);
node1.n=;
node1.f=node1.n+dis[node1.loc];
if(node1.status==aim)//入队之前先判断,可以加快速度
{
cout<<node1.path<<endl;
continue;
}
q.push(node1);
vis[node1.status]=;
bool flag=false;
while(!q.empty())
{
tnode node2=q.top();q.pop();
bool fflag=false;
for(int i=;i<;i++)
{
tnode node3=node2;
//printf("%d ",node3.loc);
if(a[i]==&&node3.loc%<=)
{
node3.puz[node3.loc]=node3.puz[node3.loc+a[i]];
node3.puz[node3.loc+a[i]]=;
node3.status=cantor(node3.puz);
}
else if(a[i]==-&&node3.loc%>=)
{
node3.puz[node3.loc]=node3.puz[node3.loc+a[i]];
node3.puz[node3.loc+a[i]]=;
node3.status=cantor(node3.puz);
}
else if(a[i]==&&node3.loc<=)
{
node3.puz[node3.loc]=node3.puz[node3.loc+a[i]];
node3.puz[node3.loc+a[i]]=;
node3.status=cantor(node3.puz);
}
else if(a[i]==-&&node3.loc>=)
{
node3.puz[node3.loc]=node3.puz[node3.loc+a[i]];
node3.puz[node3.loc+a[i]]=;
node3.status=cantor(node3.puz);
}
//printf("%d ",node3.status);
if(!vis[node3.status])
{
node3.path+=b[i];
node3.loc=node3.getloc();
node3.n++;
node3.f=node3.n+dis[node3.loc];
q.push(node3);
vis[node3.status]=;
}
if(node3.status==aim)
{
node1=node3;
fflag=true;
break;
}
}
if(fflag)
{
flag=true;
break;
}
} if(flag)
{
cout<<node1.path<<endl;
}
else
{
printf("unsolvable\n");
}
}
return ;
}
poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)的更多相关文章
- hdu3567 八数码2(康托展开+多次bfs+预处理)
Eight II Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 130000/65536 K (Java/Others)Total S ...
- POJ 1077 Eight (BFS+康托展开)详解
本题知识点和基本代码来自<算法竞赛 入门到进阶>(作者:罗勇军 郭卫斌) 如有问题欢迎巨巨们提出 题意:八数码问题是在一个3*3的棋盘上放置编号为1~8的方块,其中有一块为控制,与空格相邻 ...
- BFS(八数码) POJ 1077 || HDOJ 1043 Eight
题目传送门1 2 题意:从无序到有序移动的方案,即最后成1 2 3 4 5 6 7 8 0 分析:八数码经典问题.POJ是一次,HDOJ是多次.因为康托展开还不会,也写不了什么,HDOJ需要从最后的状 ...
- Eight POJ - 1077 HDU - 1043 八数码
Eight POJ - 1077 HDU - 1043 八数码问题.用hash(康托展开)判重 bfs(TLE) #include<cstdio> #include<iostream ...
- Poj 1077 eight(BFS+全序列Hash解八数码问题)
一.题意 经典的八数码问题,有人说不做此题人生不完整,哈哈.给出一个含数字1~8和字母x的3 * 3矩阵,如: 1 2 X 3 4 6 7 5 8 ...
- CDOJ 485 UESTC 485 Game (八数码变形,映射,逆cantor展开)
题意:八数码,但是转移的方式是转动,一共十二种,有多组询问,初态唯一,终态不唯一. 题解:初态唯一,那么可以预处理出012345678的所有转移情况,然后将初态对012345678做一个映射,再枚举一 ...
- poj 1077-Eight(八数码+逆向bfs打表)
The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've see ...
- POJ 2893 M × N Puzzle——八数码有解条件
题意:给定M*N的数码图,问能否移动到最终状态 分析 有解的判定条件可见 八数码有解条件 值得一提的是,这道题求逆序对卡树状数组,只能用归并排序. #include<cstdio> #in ...
- hdu 1043 pku poj 1077 Eight (BFS + 康拓展开)
http://acm.hdu.edu.cn/showproblem.php?pid=1043 http://poj.org/problem?id=1077 Eight Time Limit: 1000 ...
随机推荐
- MySQL 5.7 - 通过 BINLOG 恢复数据
日常开发,运维中,经常会出现误删数据的情况.误删数据的类型大致可分为以下几类: 使用 delete 误删行 使用 drop table 或 truncate table 误删表 使用 drop dat ...
- 从壹开始 [ Design Pattern ] 之二 ║ 单例模式 与 Singleton
前言 这一篇来源我的公众号,如果你没看过,正好直接看看,如果看过了也可以再看看,我稍微修改了一些内容,今天讲解的内容如下: 一.什么是单例模式 [单例模式],英文名称:Singleton Patter ...
- IDEA最常用快捷键汇总+快速写出Main函数
IDEA可以说是当下Java程序员日常开发的神器,但是想要发挥这款神器的牛逼威力,必须得熟练使用它的各种快捷键才行.本篇总结下使用IDEA(也就是IntelliJ IDEA )进行日常开发中最常用的快 ...
- HBase 基本入门
目录 一.简介 有什么特性 与RDBMS的区别 二.数据模型 三.安装HBase 四.基本使用 表操作 五.FAQ 参考文档 无论是 NoSQL,还是大数据领域,HBase 都是非常"炙热& ...
- 关于.ssh目录下的known_hosts文件的补充
一.关于.ssh目录下的known_hosts文件的补充 其实一开始是没有注意到的,按照网上的教程一步一步操作,并没有注意到这个文件的生成.直到有一次我试着去查询.ssh目录是否存在时,出现了下面的情 ...
- JavaScript笔记四
1.运算符 逻辑运算符 ! - 非运算可以对一个布尔值进行取反,true变false false边true - 当对非布尔值使用!时,会先将其转换为布尔值然后再取反 - 我们可以利用!来将其他的数据类 ...
- Django总结目录
Django总结目录 1. django框架简介及自定义简易版框架 2. 路由层 3. 视图层 4. 模板层 5. 模型层 5.1 基本操作 5.2 多表操作 5.3 进阶相关 6. 组件 6.1 a ...
- 网络OSI七层模型以及数据传输过程
网络OSI七层模型 模型图 国际标准化组织(ISO)制定了osi七层模型,iso规定了各种各样的协议,并且分了7层 每一层的详细信息 具体7层 数据格式 功能与连接方式 典型设备 应用层 Applic ...
- vue如何引入图片地址
我们在用vue时储存图片时,一般把图片放在两种文件下,一个是static文件夹下,另外一个是assets文件夹下. 下面总体说一下这两个的区别及正确的引用方式: static是放不会变动的图片(或文件 ...
- 可能是全网最简单的 OpenStack 安装方式
OpenStack 因为架构复杂,配置较多,一向以安装部署过程困难闻名.虽然 OpenStack 社区前后涌现出了很多的自动化部署工具,但是对于普通用户,特别是新人来说,上手仍然有难度. 使用本文介绍 ...