一、题目

http://acm.hdu.edu.cn/showproblem.php?pid=1043

二、两种方法

该题很明显,是一个八数码的问题,就是9宫格,里面有一个空格,外加1~8的数字,任意一种情况,如果能通过移动空格使数码组成

1 2 3
4 5 6
7 8 0

的形式,就输出变换的序列,如果不能,输出unsolvable.

逆向$BFS$+康托展开

1.什么是康托展开

https://zh.wikipedia.org/wiki/%E5%BA%B7%E6%89%98%E5%B1%95%E5%BC%80

$wiki$讲解比较好。

有了康托展开,你可能会有疑问,要它有何用?但如果你联系一下$hash$函数,康托展开能够保证每一种八数码的情况都能够用一个整型数字唯一表示,这样是不是就好理解了?

2.逆向BFS

为了求出它的变换序列,我们可以逆向思维想一下,如果我从最终结果出发去变换,然后把路途中的每一种情况都记录下来(有了康托展开对八数码进行$hash$,会很方便),记录变换路径,然后对输入的其实条件直接进行输出就可以了。

这就是逆向$BFS$的解决方案。

这里需要注意的是,如果用STL的queue以及string,可能会造成超内存。解决办法就是用个数组模拟即可。

 #include <vector>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <queue>
#include <cstring> using namespace std; const int MAXN = ;
const int fac[] = {, , , , , , , , , }; //factorial
const int dx[] = {-, , , };
const int dy[] = {, , , -};
const char op[] = "dulr"; //operation
vector<char> Path[MAXN]; //记录路径
bool Visit[MAXN]; //标记数组
struct Node
{
int S[]; //二维的数码表一维表示(下标从1开始)
int loc; //9 = x loaction
int cat; //对应的康拓展开值
};
Node Q[MAXN];
int Cantor(int s[])
{
int t, ans = ;
for(int i = ; i < ; i++)
{
t = ;
for(int j = i+; j < ; j++)
{
if(s[j] < s[i])
t++;
}
ans += t*fac[-i-];
}
return ans;
} void BFS()
{
memset(Visit, , sizeof(Visit));
int x, y, Cnt = , Rea = ;
Node cur, t;
for(int i = ; i < ; i++)
cur.S[i] = i+;
cur.S[] = ;
cur.loc = ;
cur.cat = Cantor(cur.S);
//Path[cur.cat] = "";
Visit[cur.cat] = ;
Q[Cnt++] = cur; while(Rea < Cnt)
{ t = Q[Rea];
Rea++;
for(int i = ; i < ; i++)
{
x = t.loc/ + dx[i];
y = t.loc% + dy[i];
if(x < || x > || y < || y > )
continue;
cur = t; //**
cur.loc = x*+y;
cur.S[t.loc] = t.S[cur.loc]; //交换
cur.S[cur.loc] = ; //X
cur.cat = Cantor(cur.S);
if(!Visit[cur.cat])
{
Visit[cur.cat] = ;
Path[cur.cat] = Path[t.cat];
Path[cur.cat].push_back(op[i]);
//Path[cur.cat] = op[i] + Path[t.cat];
Q[Cnt++] = cur; }
}
}
} int main()
{
//freopen("input.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int s[];
char c[];
BFS(); while(scanf("%s", c)!=EOF)
{
if(c[] == 'x')
s[] = ;
else
s[] = c[] - '';
for(int i = ; i < ; i++)
{
scanf("%s", c);
if(c[] == 'x')
s[i] = ;
else
s[i] = c[] - '';
}
int Cat = Cantor(s);
if(Visit[Cat])
{
for(int i = Path[Cat].size()-; i >= ; i--)
printf("%c", Path[Cat][i]);
printf("\n");
}
else
printf("unsolvable\n");//cout << "unsolvable" << endl;
}
return ;
}

AC代码

3.A*

http://www.cnblogs.com/me-sa/archive/2010/05/18/A-Star-Pathfinding-for-Beginners.html

基本A*算法的入门讲解都是这个。其实当你认真体会后,A*算法的关键就是F=G+H中的G,H如何算的问题,其他的与搜索大同小异,因为有了G,H,就可以有目的性的去搜索,也就是启发式搜索。(仅个人理解)

这个题目里,求H依然采用的是曼哈顿距离,即每个每个数从当前位置到它最终位置的曼哈顿距离。

这里,还用到了小技巧,就是逆序数,当在满足上述约定的八数码问题中,空格与相邻棋子的交换不会改变棋局中棋子数列的逆序数的奇偶性。因为最终情况的逆序数是偶数,所以要保证每次搜索过程中逆序数都是偶数。这样就达到了剪枝。

需要注意的是,求逆序数,无论空格是代表的0还是9,都不要考虑进去。

推广一下:对于N*M数码问题,空白在同一行交换不会导致奇偶性互变;上下行交换,如果列为奇数,则不会导致奇偶性互变;如果列数为偶数,则会导致奇偶性互变,所以此时还要考虑上下行交换的次数,综合得出答案。

 /*逆向BFS*/
/*
#include <vector>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <queue>
#include <cstring> using namespace std; const int MAXN = 370000;
const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880}; //factorial
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, 1, -1};
const char op[] = "dulr"; //operation
vector<char> Path[MAXN]; //记录路径
bool Visit[MAXN]; //标记数组
struct Node
{
int S[9]; //二维的数码表一维表示(下标从1开始)
int loc; //9 = x loaction
int cat; //对应的康拓展开值
};
Node Q[MAXN];
int Cantor(int s[])
{
int t, ans = 1;
for(int i = 0; i < 9; i++)
{
t = 0;
for(int j = i+1; j < 9; j++)
{
if(s[j] < s[i])
t++;
}
ans += t*fac[9-i-1];
}
return ans;
} void BFS()
{
memset(Visit, 0, sizeof(Visit));
int x, y, Cnt = 0, Rea = 0;
Node cur, t;
for(int i = 0; i < 8; i++)
cur.S[i] = i+1;
cur.S[8] = 0;
cur.loc = 8;
cur.cat = Cantor(cur.S);
//Path[cur.cat] = "";
Visit[cur.cat] = 1;
Q[Cnt++] = cur; while(Rea < Cnt)
{ t = Q[Rea];
Rea++;
for(int i = 0; i < 4; i++)
{
x = t.loc/3 + dx[i];
y = t.loc%3 + dy[i];
if(x < 0 || x > 2 || y < 0 || y > 2)
continue;
cur = t; //**
cur.loc = x*3+y;
cur.S[t.loc] = t.S[cur.loc]; //交换
cur.S[cur.loc] = 0; //X
cur.cat = Cantor(cur.S);
if(!Visit[cur.cat])
{
Visit[cur.cat] = 1;
Path[cur.cat] = Path[t.cat];
Path[cur.cat].push_back(op[i]);
//Path[cur.cat] = op[i] + Path[t.cat];
Q[Cnt++] = cur; }
}
}
} int main()
{
//freopen("input.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int s[10];
char c[2];
BFS(); while(scanf("%s", c)!=EOF)
{
if(c[0] == 'x')
s[0] = 0;
else
s[0] = c[0] - '0';
for(int i = 1; i < 9; i++)
{
scanf("%s", c);
if(c[0] == 'x')
s[i] = 0;
else
s[i] = c[0] - '0';
}
int Cat = Cantor(s);
if(Visit[Cat])
{
for(int i = Path[Cat].size()-1; i >= 0; i--)
printf("%c", Path[Cat][i]);
printf("\n");
}
else
printf("unsolvable\n");//cout << "unsolvable" << endl;
}
return 0;
}
2 3 4 1 5 0 7 6 8
2 3 0 1 5 4 7 6 8
90747
2 3 4 1 5 0 7 6 8
92307 */ /* A* */ #include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <fstream> using namespace std; const int MAXN = ;
const int fac[] = {, , , , , , , , , }; //factorial
const int dx[] = {, , , -};
const int dy[] = {, -, , };
const char op[] = "rldu"; //operation
const int Aim = ;
int Pre[MAXN];
char Vp[MAXN];
bool Visit[MAXN];
struct Node
{
int s[];
int cat;
int g, h;
int loc; bool operator < (const Node &t)const
{
if(h == t.h)
return g > t.g;
return h > t.h;
}
}; int getCator(const int s[])
{
int t, ans = ;
for(int i = ; i < ; i++)
{
t = ;
for(int j = i+; j < ; j++)
{
if(s[j] < s[i])
t++;
}
ans += t*fac[-i-];
}
return ans; } int getH(const int s[])
{
int x, y, x0, y0, ans = ;
for(int i = ; i < ; i++)
{
if(s[i])
{
x0 = i/, y0 = i%;
x = (s[i]-)/, y = (s[i]-)%; //这里要注意
ans += abs(x-x0) + abs(y-y0); //曼哈顿距离
}
}
return ans;
} bool judge(const int S[]) //判断逆序对数是否为偶数
{
int ans = ;
for(int i = ; i < ; i++)
{
for(int j = i+; j < ; j++)
{
if( S[j] && S[i] && S[j] < S[i] )
ans++;
}
}
if(ans% == )
return true;
else
return false;
} void astar(Node cur)
{
memset(Visit, , sizeof(Visit));
memset(Pre, -, sizeof(Pre));
int x, y;
priority_queue<Node> PQ;
PQ.push(cur);
Visit[cur.cat] = ;
Pre[cur.cat] = -; while(!PQ.empty())
{
Node t = PQ.top();
PQ.pop();
for(int i = ; i < ; i++)
{
x = t.loc/ + dx[i];
y = t.loc% + dy[i];
if(x < || x > || y < || y > )
continue;
cur = t;
cur.loc = x* + y;
cur.s[t.loc] = t.s[cur.loc];
cur.s[cur.loc] = ;
cur.cat = getCator(cur.s); if(Visit[cur.cat] == && judge(cur.s))
{ Visit[cur.cat] = ;
cur.h = getH(cur.s);
cur.g++;
Pre[cur.cat] = t.cat;
Vp[cur.cat] = op[i];
if(cur.cat == Aim)
return;
PQ.push(cur);
}
}
}
} void Print()
{
int c = Aim;
string ans = "";
while(Pre[c] != -)
{
ans = Vp[c]+ans;
c = Pre[c];
}
cout << ans << endl;
} int main()
{
//freopen("input.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
Node cur;
char c[];
while(scanf("%s", c)!=EOF)
{
if(c[] == 'x')
{
cur.s[] = ;
cur.loc = ;
}
else
cur.s[] = c[] - '';
for(int i = ; i < ; i++)
{
scanf("%s", c);
if(c[] == 'x')
{
cur.s[i] = ;
cur.loc = i;
}
else
cur.s[i] = c[] - '';
} if(!judge(cur.s))
{
printf("unsolvable\n");
continue;
}
cur.cat = getCator(cur.s);
cur.g = , cur.h = getH(cur.s);
astar(cur);
Print();
}
return ;
}

AC代码

HDU_1043 Eight 【逆向BFS + 康托展开 】【A* + 康托展开 】的更多相关文章

  1. HDU1043 Eight(八数码:逆向BFS打表+康托展开)题解

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

  2. 康托展开&逆康托展开学习笔记

    啊...好久没写了...可能是最后一篇学习笔记了吧 题目大意:给定序列求其在全排列中的排名&&给定排名求排列. 这就是康托展开&&逆康托展开要干的事了.下面依次介绍 一 ...

  3. hdu1043 经典的八数码问题 逆向bfs打表 + 逆序数

    题意: 题意就是八数码,给了一个3 * 3 的矩阵,上面有八个数字,有一个位置是空的,每次空的位置可以和他相邻的数字换位置,给你一些起始状态 ,给了一个最终状态,让你输出怎么变换才能达到目的. 思路: ...

  4. 康拓展开 & 逆康拓展开 知识总结(树状数组优化)

    康拓展开 : 康拓展开,难道他是要飞翔吗?哈哈,当然不是了,康拓具体是哪位大叔,我也不清楚,重要的是 我们需要用到它后面的展开,提到展开,与数学相关的,肯定是一个式子或者一个数进行分解,即 展开. 到 ...

  5. hdoj1043 Eight(逆向BFS+打表+康拓展开)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1043 思路: 由于自己对康拓展开用的太少,看到这个题没想到康拓展开,最开始打算直接转换为数字,但太占内 ...

  6. 康托(Cantor)展开

    直接进入正题. 康托展开 Description 现在有"ABCDEFGHIJ”10个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的? Input ...

  7. Luogu5367 【模板】康托展开 (康拓展开)

    \(n^2\)暴力 #include <iostream> #include <cstdio> #include <cstring> #include <al ...

  8. 逆向bfs搜索打表+康拓判重

    HDU 1043八数码问题 八数码,就是1~8加上一个空格的九宫格,这道题以及这个游戏的目标就是把九宫格还原到从左到右从上到下是1~8然后最后是空格. 没了解康托展开之前,这道题怎么想都觉得很棘手,直 ...

  9. 【算法系列学习三】[kuangbin带你飞]专题二 搜索进阶 之 A-Eight 反向bfs打表和康拓展开

    [kuangbin带你飞]专题二 搜索进阶 之 A-Eight 这是一道经典的八数码问题.首先,简单介绍一下八数码问题: 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的 ...

随机推荐

  1. Python代码注释

    1.单行注释使用# # Code 2.多行注释,成对使用'''或""",三个单撇号或三个双引号 “”” Code “”” 3.多行快捷注释 1).增加注释 选中待注释的多 ...

  2. pandas 中的 多条件分割, list 排序

    main_comment_num_3m and avg_group_order_cnt_12m = 0.863230main_comment_score_1m and avg_group_order_ ...

  3. js-day

    1.克莱托指数 公式 :体重(kg) / (身高(m) * 身高(m)) < 20 : 偏瘦 > 20 <25 : 正常 > 25 : 偏旁 步骤: 1.输入体重(weight ...

  4. 【Web API2】ASP.NET Web API Security

    实现安全的方式既可以是host提供,也可以框架提供. 1,HTTP Module 方式,工作在IIS上,所以web api要托管在IIS上才行.其作用于HTTP管道的最前端,所以这种方式影响的是全局, ...

  5. Ubuntu安装开发版pidgin支持lwqq插件

    sudo add-apt-repository ppa:lainme/pidgin-lwqq  """添加pidgin-lwqq源""" s ...

  6. 初学reactNative遇到的问题总结

    1.undefined is not an object (evaluating '_react3.default.PropTypes.shape') Navigator已经不再react nativ ...

  7. windows phone之依赖属性(DependencyProperty)

    Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能,这些服务通常统称为 WPF 属性系统.由 WPF ...

  8. Windows + python + pywinauto 搭建自动化测试环境

    最近公司在搞测试, 单纯的人工去测试需要花费太多的人力物力以及时间, 所以准备用Python做一套自动化测试来使用. 本文中使用的是Python3.6.8  和 pywin32-224.win-amd ...

  9. .NET MVC对接POLYV——HTML5播放器播放加密视频

    官方参考文档:http://dev.polyv.net/2017/videoproduct/v-playerapi/html5player/html5-docs/ 1.上传视频之前根据自己需要对所上传 ...

  10. 【kudu pk parquet】TPC-H Query2对比解析

    这是[kudu pk parquet]的第二篇,query2在kudu和parquet上的对比解析,其中kudu包含有不能下发的谓词. 3台物理机,1T规模的数据集,impala和kudu版本是我们修 ...