题目大意:裸的八数码问题,让你输出空格的一条合法移动路径

首先利用康托展开对排列编号,可以预处理出排列,就不必逆展开了

然后利用A*算法求解

A*算法是一种启发式搜索,具体实现要用到优先队列/堆,不同于$dijkstra$,它的堆不是按照 初始状态当前状态的花费$dis_{i}$进行贪心转移,而是额外处理出一个估值函数,处理出当前状态目标状态花费的估计值$h_{i}$,然后按照$dis_{i}+h_{i}$排序,优先取出总和最小的。并且每个状态只会被搜索一次。如果搜索到了目标状态,立即跳出

这个过程,相当于我们取出一个全局较优解,虽然不一定是最优的,但大量减少了无用的搜索时间

而这道题的估值函数,可以粗略的计算为,每个数 当前状态的位置 到 目标状态的位置 的曼哈顿距离$(|xj-xi|+|yj-yi|)$ 之和

经过了A*优化的搜索,虽然不一定能搜出最短的路径,但极大减少了搜索的时间,因为搜的路径越短,我们搜索的总时间也越短,几乎是指数级别的优化。

然而A*也有弊端,如果存在无解的情况,A*就会退化成BFS,对所有状态都搜一次,而且多了一个常数$log$,在hdu上会T掉

所以我上网查了用了八数码判无解的方法,即对于去掉空格以后的8个数的排列,如果逆序对数量总和是奇数,则无解

证明大概是这样的,对于一个序列,任意交换两个相邻的数,逆序对数量的奇偶性不变,而八数码的表格扔到序列上,也满足这个性质

搞了大半个下午终于过了

 #include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define NN 370010
#define MM 100
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define inf 0x3f3f3f3f
using namespace std; struct node{
int id,d;
friend bool operator<(const node &s1,const node &s2)
{return s1.d>s2.d;}
node(int id,int d):id(id),d(d){}
node(){}
};
int xx[]={-,,,},yy[]={,,,-};
int f[NN][],vis[NN],p[][],cnt;
int h[NN],g[NN],fa[NN],d[NN];
int S[],use[],now[],tmp[],mu[];
inline int mabs(int x){return x>=?x:-x;}
char ans[MM],c[]={'u','r','d','l'}; void dfs_permu(int dep)
{
if(dep==){
cnt++;
for(int i=;i<=;i++)
f[cnt][i]=now[i],
h[cnt]+=(mabs((i-)/-(now[i]-)/)+mabs((i-)%-(now[i]-)%));
return;
}
for(int i=;i<=;i++)
if(!use[i]){
use[i]=,now[dep]=i;
dfs_permu(dep+);
use[i]=,now[dep]=;
}
}
bool check(int x,int y)
{if(x<||y<||x>||y>)return ;return ;}
void Pre()
{
dfs_permu();
mu[]=;
for(int i=;i<=;i++)
mu[i]=mu[i-]*i;
}
int s[];
void update(int x,int w){for(int i=x;i<=;i+=(i&(-i)))s[i]+=w;}
int query(int x){int ans=;for(int i=x;i>;i-=(i&(-i)))ans+=s[i];return ans;}
int canter(int *tmp)
{
int ans=;
for(int i=;i<=;i++)
update(i,);
for(int i=;i<=;i++){
ans+=query(tmp[i]-)*mu[-i];
update(tmp[i],-);
}
return ans+;
}
int clr[NN],nn;
void init()
{
memset(ans,,sizeof(ans));
for(int i=;i<=nn;i++){
vis[clr[i]]=fa[clr[i]]=d[clr[i]]=;
g[clr[i]]=inf;
}nn=;
} int main()
{
Pre();
char str[];
priority_queue<node>q;
nn=cnt;
for(int i=;i<=nn;i++) clr[i]=i;
while(scanf("%s",str)!=EOF)
{ if(''<=str[]&&str[]<='')
S[]=str[]-'';
else S[]=;
for(int i=;i<=;i++)
{
scanf("%s",str);
if(''<=str[]&&str[]<='')
S[i]=str[]-'';
else S[i]=;
}
int invcnt=;
for(int i=;i<=;i++)
for(int j=;j<i;j++){
if(S[i]==||S[j]==) continue;
if(S[j]>S[i]) invcnt++;
}
if(invcnt&){
printf("unsolvable\n");
continue;}
init();
int s,t,st,sx,sy,x,y;
st=canter(S);
if(st==){
puts("");
continue;
}
q.push(node(st,h[st]));
g[st]=;
while(!q.empty())
{
node k=q.top();q.pop();
s=k.id;clr[++nn]=s;
if(s==) break;
if(vis[s]) continue; vis[s]=;
for(int i=;i<=;i++)
if(f[s][i]==)
sx=(i-)/+,sy=(i-)%+;
for(int i=;i<=;i++)
tmp[i]=f[s][i];
for(int k=;k<;k++)
{
x=sx+xx[k],y=sy+yy[k];
if(!check(x,y)) continue;
swap(tmp[(x-)*+y],tmp[(sx-)*+sy]);
int t=canter(tmp);
if(g[t]>g[s]+&&!vis[t]){
g[t]=g[s]+,fa[t]=s,d[t]=k;
q.push(node(t,g[t]+h[t]));
}
swap(tmp[(x-)*+y],tmp[(sx-)*+sy]);
}
}
while(!q.empty()){
node k=q.top();q.pop();
s=k.id;clr[++nn]=s;
}
if(!fa[]){
printf("unsolvable\n");
continue;
}
t=;int num=;
while(t!=st){
ans[++num]=c[d[t]];
t=fa[t];
}
for(int i=num;i>=;i--)
printf("%c",ans[i]);
puts(""); } return ;
}

HDU 1043 Eight (A*算法)的更多相关文章

  1. POJ 1077 && HDU 1043 Eight A*算法,bfs,康托展开,hash 难度:3

    http://poj.org/problem?id=1077 http://acm.hdu.edu.cn/showproblem.php?pid=1043 X=a[n]*(n-1)!+a[n-1]*( ...

  2. HDU 1043 Eight 八数码问题 A*算法(经典问题)

    HDU 1043 Eight 八数码问题(经典问题) 题意 经典问题,就不再进行解释了. 这里主要是给你一个状态,然后要你求其到达\(1,2,3,4,5,6,7,8,x\)的转移路径. 解题思路 这里 ...

  3. POJ-1077 HDU 1043 HDU 3567 Eight (BFS预处理+康拓展开)

    思路: 这三个题是一个比一个令人纠结呀. POJ-1077 爆搜可以过,94ms,注意不能用map就是了. #include<iostream> #include<stack> ...

  4. HDU 1043 Eight(八数码)

    HDU 1043 Eight(八数码) 00 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)   Problem Descr ...

  5. Eight POJ - 1077 HDU - 1043 八数码

    Eight POJ - 1077 HDU - 1043 八数码问题.用hash(康托展开)判重 bfs(TLE) #include<cstdio> #include<iostream ...

  6. HDU - 1043 - Eight / POJ - 1077 - Eight

    先上题目: Eight Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Tota ...

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

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

  8. ACM: HDU 1869 六度分离-Dijkstra算法

    HDU 1869六度分离 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Descri ...

  9. HDU 1532 (Dinic算法)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1532 题目大意: 就是由于下大雨的时候约翰的农场就会被雨水给淹没,无奈下约翰不得不修建水沟,而且是网络 ...

随机推荐

  1. CF1038D Slime 构造

    题目大意: 有nnn只史莱姆qwq,每只史莱姆有一个分数,每次一只史莱姆可以吞掉左边的或者右边的史莱姆(要是有的话),然后ta的分数会减去被吞的史莱姆的分数,问最后剩下的史莱姆分数最大为多少 输入格式 ...

  2. [BOI2007]摩基亚

    题目:洛谷P4390.BZOJ1176. 题目大意: 给你一个\(W\times W\)的矩阵,初始每个数都为\(S\).现在有若干操作: 1. 给某个格子加上一个值:2. 询问某个子矩阵的值的和:3 ...

  3. [JZOJ]100047. 【NOIP2017提高A组模拟7.14】基因变异

    21 世纪是生物学的世纪,以遗传与进化为代表的现代生物理论越来越多的 进入了我们的视野. 如同大家所熟知的,基因是遗传因子,它记录了生命的基本构造和性能. 因此生物进化与基因的变异息息相关,考察基因变 ...

  4. "随笔"列表 - 按时间先后顺序排列

    这是一个测试 linux采用scp命令拷贝文件到本地,拷贝本地文件到远程服务器 美化博客园 virtual box虚拟机在linux下设置共享文件夹 纯净版linux (debian)挂载Virtua ...

  5. 洛谷 P1147 连续自然数和 (滑动窗口)

    维护一个滑动窗口即可 注意不能有m到m的区间,因为区间长度要大于1 #include<cstdio> #define _for(i, a, b) for(int i = (a); i &l ...

  6. Vue轮播图插件---Vue-Awesome-Swiper

    轮播图插件 Vue-Awesome-Swiper 地址:https://github.com/surmon-china/vue-awesome-swiper 安装:npm install vue-aw ...

  7. sql查询语句中on和where的区别

    sql中的连接查询分为3种, cross join,inner join,和outer join ,  在 cross join和inner join中,筛选条件放在on后面还是where后面是没区别 ...

  8. 利用新版本自带的Zookeeper搭建kafka集群

    安装简要说明新版本的kafka自带有zookeeper,其实自带的zookeeper完全够用,本篇文章以记录使用自带zookeeper搭建kafka集群.1.关于kafka下载kafka下载页面:ht ...

  9. mysql字符集修改(ubuntu)

    1.关闭mysql服务 /etc/init.d/mysql start|stop 2.在/etc/mysql/my.cnf,添加下列信息 [client] default-character-set= ...

  10. 字符串的HashCode可能相同

    字符串的HashCode可能相同 学习了:http://blog.csdn.net/hl_java/article/details/71511815