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

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. c的scanf为什么比c++的cin快

    很早就知道,c的scanf(printf)比c++的快.刷题时尤其明显,在这上面超时是常有的事儿. 但,这是别人告诉我的,c快. 为什么快? 从网上借鉴一个例子做个简单测试: 1.cpp     // ...

  2. HDU - 5094 Maze(状压+bfs)

    Maze This story happened on the background of Star Trek. Spock, the deputy captain of Starship Enter ...

  3. Django 之 requirement.txt 依赖文件生成

    通过依赖文件,别人在使用我们的项目时,不需要再一个个去安装所需模块,只需安装依赖文件即可. 1. 导出整个虚拟环境依赖 # 在项目根目录中,打开终端执行以下命令 # 生成 requirements.t ...

  4. VisualStudio2017中新建的ASP.NET Core项目中的各个文件的含义

     Program.cs is the entry point for the web application; everything starts from here. As we mentione ...

  5. 从网络架构方面简析循环神经网络RNN

    一.前言 1.1 诞生原因 在普通的前馈神经网络(如多层感知机MLP,卷积神经网络CNN)中,每次的输入都是独立的,即网络的输出依赖且仅依赖于当前输入,与过去一段时间内网络的输出无关.但是在现实生活中 ...

  6. int **指针问题

    转自:http://blog.csdn.net/u012501459/article/details/45395571 在打印二维数组时遇到了问题,二维数组可以这样定义int matrix[ROWS] ...

  7. Python实现栈、队列

    目录 1. 栈的Python实现 1.1 以列表的形式简单实现栈 1.2 以单链表形式实现栈 2. 队列的Python实现 2.1 以列表实现简单队列 2.2 以单链表形式实现队列   本文将使用py ...

  8. ORM应用

    目录 ORM概念 ORM由来 ORM的优势 ORM的劣势 ORM总结 ORM 与 DB 的对应关系图 Model 模块 ORM操作 增删改查操作 ORM概念 对象关系映射(Object Relatio ...

  9. JAVA接口详细讲解

    接口 接口的概念  接口代表的是一个功能的集合,定义规范,所有的方法都是抽像方法,这是一种思想是一种规则,将这个种规则称为接口. 接口的定义 使用关键字 interface 叫做接口 修饰符 inte ...

  10. P1101 单词方阵(DFS)

    题目描述 给一n \times nn×n的字母方阵,内可能蕴含多个"yizhong"单词.单词在方阵中是沿着同一方向连续摆放的.摆放可沿着 88个方向的任一方向,同一单词摆放时不再 ...