一道关乎人生完整的问题。

DBFS的优越:避免了结点膨胀太多。

假设一个状态结点可以扩展m个子结点,为了简单起见,假设每个结点的扩展都是相互独立的。

分析:起始状态结点数为1,每加深一层,结点数An = An-1*m。假如搜索了i层找到终点,那么经过的结点数是O(i^m),如果从两边同时搜索,结点数是O(i^(m/2))。

极端情况,终点完全封闭。

DBFS的正确姿势:

图片来源:http://www.cppblog.com/Yuan/archive/2011/02/23/140553.aspx

下面是原博客上的分析:

交替结点可能会因为扩展顺序而认为s-1-5-3-t是最短路。//这也可能得到正确结果,与结点的扩展顺序有关系

然而交替层次的做法才是正确的。

优化:提供速度的关键在于使状态扩展得少一些,所以优先选择队列长度较少的去扩展,保持两边队列长度平衡。这比较适合于两边的扩展情况不同时,一边扩展得快,一边扩展得慢。如果两边扩展情况一样时,加了后效果不大,不过加了也没事。

----------------------------------分割线------------------------------------------------------

DBFS的代码也是磕了好久想到怎么实现的。

加了奇偶剪枝和一些小优化

更新。经过仔细思考,简化了代码。

/*
Created by Rey Chen on 2015.7.5
*/ #include<bits/stdc++.h>
using namespace std; //#define local
const int maxn = ;
int vis1[maxn];
int vis2[maxn];//保存距离起点的距离,初始值-1
int fac[]; struct node
{
int e[];
int p;
int cod;//避免二次计算,初始值为-1
int code() {
if(~cod) return cod;
int Hash = ;
for(int i = ; i < ; i++) {
int cnt = ;
for(int j = i+; j < ; j++)
if(e[j] < e[i]) cnt++;
Hash += fac[-i] * cnt;
}
return cod = Hash;
}
int rev_value(){//用于奇偶剪枝
int res = , cnt ,i ,j;
for(i = ; i < ; i++) {
if(e[i]) {
cnt = ;
for(j = i+; j < ; j++)
if(e[j] && e[j] < e[i]) cnt++;
res += cnt;
}
}
return res;
}
}; node start;
node ed;
int edHash;
int nodesz;
typedef vector<node>* vnodep;
vector<node> v1;
vector<node> v2;
vector<node> v3;
vector<node>:: iterator it,tmp_it; const int dx[] = {-, , , };
const int dy[] = { , ,-, };
bool ilegal[][];
void solve()
{
if(start.rev_value()&) { puts("unsolvable"); return; }//忽略0,操作不会改变逆序数总数的奇偶性。
int t;
t = start.code();
if(t == edHash) { puts("");return;}
memset(vis1,-,sizeof(vis1) );
memset(vis2,-,sizeof(vis2) );
vis1[t] = ;
vis2[edHash] = ; v1.clear(); v2.clear(); v3.clear();
vnodep q1 = &v1, q2 = &v2, nxt = &v3;
q1->push_back(start);
q2->push_back(ed);
int *V1 = vis1, *V2 = vis2;
while( !q1->empty() && !q2->empty() ) {
if(q1->size() > q2->size()) swap(q1,q2),swap(V1,V2); //化简代码的小技巧
for(it = q1->begin(), tmp_it = q1->end(); it != tmp_it ; it++){
node& u = *it;
node v;
for(int i = ;i < ;i++){
if(ilegal[u.p][i]) continue;
int np = u.p + dx[i]* + dy[i];
memcpy(&v,&u,nodesz); v.cod = -;//memcpy 比直接赋值要快
swap(v.e[np],v.e[u.p]);
if(!~V1[t = v.code()]){
V1[t] = V1[u.code()] + ;
if(~V2[t]){ printf("%d\n",V2[t]+V1[t]); return; }
v.p = np;
nxt->push_back(v);
}
}
}
q1->clear();
swap(q1,nxt);
}
puts("unsolvable");
} void init(){
fac[] = ;
for(int i = ; i < ; i++)
fac[i] = fac[i-]*i;
for(int i = ; i < ; i++)
for(int j = ; j < ; j++){
for(int k = ; k < ; k++)
if( (i == && k == ) || (i == && k == ) || (j == && k == ) || (j == && k == ) )
ilegal[i*+j][k] = true;
else ilegal[i*+j][k] = false;
}
} int main()
{
#ifdef local
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif // local
char ch,s[];
init();
for(int i = ; i < ;i ++)
ed.e[i] = i+;
ed.e[] = ;
ed.p = ;
ed.cod = -;
edHash = ed.code();
nodesz = sizeof(ed); while(gets(s)) {
int j = ;
for(int i = ; i < ; i ++, j++) {
while(sscanf(s+j,"%c",&ch),ch == ' ')j++;
if(ch == 'x'){
start.e[i] = ; start.p = i;
}else {
start.e[i] = ch - '';
}
}
start.cod = -;
solve();
}
return ;
}

花了点时间写了A*。A*的关键在于估价函数,估价函数必须要小于实际值,越接近越好。

这里取的是除去x以后的曼哈顿距离。

这题为什么不需要两个表?需要open表是因为有可能有捷径的出现,这题不需要。

#include<bits/stdc++.h>
using namespace std; int t[],s[],Zero,tHashCode;
int fac[];
struct node
{
int p[], z, f, dist, hashCode;
bool operator < (const node& rhs) const {
return f > rhs.f || (f == rhs.f && dist > rhs.dist);
}
}; inline int Hash(int *a)
{
int ans = ;
for(int i = ; i < ; i++) {
int cnt = ;
for(int j = i+; j < ; j++) if(a[j] < a[i]) cnt++;
ans += fac[-i] * cnt;
}
return ans;
} inline int Rev_value(int *a){
int ans = ;
for(int i = ; i < ; i++) {
int cnt = ;
for(int j = i+; j < ; j++) if(a[j] && a[j] < a[i]) cnt++;
ans += cnt;
}
return ans;
}
int Cost[][]; //除去x之外到目标的网格距离和
//x 和 其他数交换,理想情况每次距离减一
inline void Manhattan(node &A)
{
A.f = A.dist;
for(int i = ; i < ; i++)if(A.p[i])
A.f += Cost[i][A.p[i]-];
} bool vis[];
const int dx[] = {-, , , };
const int dy[] = { , ,-, };
int dz[];
bool ilegal[][]; void AstarBfs()
{
if(Rev_value(s)&) { puts("unsolvable"); return; }
node u;
u.hashCode = Hash(s);
if(u.hashCode == tHashCode) {puts(""); return;}
memset(vis,,sizeof(vis));
vis[u.hashCode] = ;
memcpy(u.p,s,sizeof(s));
u.dist = ;
u.z = Zero;
priority_queue<node> q;
Manhattan(u);
q.push(u);
while(q.size()) {
u = q.top(); q.pop();
if(u.hashCode == tHashCode) {printf("%d\n",u.dist);return;}
node v;
for(int i = ; i < ; i++) {
if(ilegal[u.z][i]) continue;
v.z = u.z + dz[i];
memcpy(v.p,u.p,sizeof(u.p));
swap(v.p[v.z],v.p[u.z]);
v.hashCode = Hash(v.p);
if(vis[v.hashCode]) continue;
vis[v.hashCode] = ;
v.dist = u.dist +;
Manhattan(v);
q.push(v);
}
}
puts("unsolvable");
} void init()
{
for(int i = ; i < ; i++)
for(int j = ; j < ; j++)
Cost[i][j] = (abs(i/-j/) + abs(i%-j%));
for(int i = ; i < ; i++)
t[i] = i+;
t[] = ;
fac[] = ;
for(int i = ; i < ; i++)
fac[i] = fac[i-]*i;
tHashCode = Hash(t);
for(int i = ; i < ; i++)
for(int j = ; j < ; j++)
for(int k = ; k < ; k++){
int nx = i+dx[k], ny = j + dy[k];
ilegal[i*+j][k] = !(nx>= && nx < && ny >= && ny < );
}
for(int k = ; k < ; k++) dz[k] = dx[k]* + dy[k];
} int main()
{
init();
char str[];
while(fgets(str,,stdin)) {
int j = ;
for(int i = ; i < ; i++, j++){
char ch;
while(sscanf(str+j,"%c",&ch),ch == ' ')j++;
if(ch == 'x'){
s[i] = ;Zero = i;
}else {
s[i] = ch - '';
}
}
AstarBfs();
}
return ;
}

附上数据生成器

#include<bits/stdc++.h>
using namespace std; int main()
{
srand( time( NULL ) );
char s[] ;
char ori[] = "";
int n = ;
int m = ;
int init = ;
for(int i = ; i < ; i++) next_permutation(ori,ori+);
for(int i=;i<n;i++)
{
for(int j = ;j<m;j++){
strcpy(s,ori);
s[] = 'x';s[] = '\0';
swap(s[],s[rand()%]);
for(int k = ;k < ; k++)
printf("%c%c",s[k],k==?'\n':' ');
}
next_permutation(ori,ori+);
} }

.bat

:loop
make.exe>data.txt
std.exe<data.txt>std.txt
my<data.txt>my.txt
fc my.txt std.txt
if not errorlevel 1 goto loop
pause

cdoj 414 八数码 (双向bfs+康拓展开,A*)的更多相关文章

  1. Eight (HDU - 1043|POJ - 1077)(A* | 双向bfs+康拓展开)

    The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've see ...

  2. 8数码,欺我太甚!<bfs+康拓展开>

    不多述,直接上代码,至于康拓展开,以前的文章里有 #include<iostream> #include<cstdio> #include<queue> using ...

  3. hdu3567 八数码2(康托展开+多次bfs+预处理)

    Eight II Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 130000/65536 K (Java/Others)Total S ...

  4. 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 ...

  5. HDU 1043 Eight(双向BFS+康托展开)

    http://acm.hdu.edu.cn/showproblem.php?pid=1043 题意:给出一个八数码,求出到达指定状态的路径. 思路:路径寻找问题.在这道题里用到的知识点挺多的.第一次用 ...

  6. poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)

    题目来源: http://poj.org/problem?id=1077 题目大意: 给你一个由1到8和x组成的3*3矩阵,x每次可以上下左右四个方向交换.求一条路径,得到12345678x这样的矩阵 ...

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

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

  8. 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 ...

  9. HDU 4531 bfs/康拓展开

    题目链接http://acm.hdu.edu.cn/showproblem.php?pid=4531 吉哥系列故事——乾坤大挪移 Time Limit: 2000/1000 MS (Java/Othe ...

随机推荐

  1. JavaWeb_tomcat设置默认应用

    在tomcat的server.xml文件中设置默认应用. 在tomcat文件目录里面的conf/server.xml文件中,在<Engine>...</Engine>中再增加一 ...

  2. Automake使用说明

    说明 从零开始编写automake工程非常复杂也没有必要,我们只要能看懂开源项目的automake即可,然后根据自己实际情况进行修改即可,下面给出两个比较好的参考项目,其中spice-gtk涵盖了使用 ...

  3. 渲染路径-surface shader 光照函数与渲染路径

    https://docs.unity3d.com/Manual/SL-SurfaceShaderLighting.html Lighting Model declaration Lighting mo ...

  4. CF1088F Ehab and a weird weight formula【倍增】

    首先把点权归到边上,设点权较小的一个点是v,也就是(u,v)的边权是log2(dis(u,v))*a[v]+a[v]+a[u] 然后还有一个性质就是这棵树按点权最小点提起来就是一个堆 暴力是n^2的M ...

  5. nginx 初了解

    随着现代web开发的发展,restful,前后端分离,前端js框架的应用越来越普遍.很多web应用请求的接口可能根本就存在于不同的服务器,类似于微信,支付宝等等.这其中就会存在跨域的问题.简单来说,跨 ...

  6. hyperledger fabric 1.0.5 分布式部署 (九)

    linux 使用vim.ctags 配置fabric 源码阅读环境 首先需要安装 ctags,作者使用apt-get 来安装的,安装的版本是5.9 apt-get install ctags 5.9 ...

  7. java基础第十篇之异常

    1.1接口概念 类:具有相同属性和功能的事物集合 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”. 接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于 ...

  8. Sublime Text 3 配置 sass

    先安装Sublime Text的sass 和 sass build插件, Sublime Text新建一个test.scss文件 $color: #369;  body {     backgroun ...

  9. jquery jtemplates.js模板渲染引擎的详细用法第一篇

    jquery jtemplates.js模板渲染引擎的详细用法第一篇 Author:ching Date:2016-06-29 jTemplates是一个基于JQuery的模板引擎插件,功能强大,有了 ...

  10. 常用CMD指令

    快捷方式: dcomcnfg.exe   打开windows的组件服务. regedit   打开windows的注册表的界面,进行管理. services.msc  打开service面板 calc ...