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

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. ACM-ICPC2018徐州网络赛 Features Track(二维map+01滚动)

    Features Track 31.32% 1000ms 262144K   Morgana is learning computer vision, and he likes cats, too. ...

  2. DOTween Sequence 使用图解

    http://blog.csdn.net/jiejieup/article/details/41521577 最近在使用DOTween制作一些动画过渡的内容,发现非常好用,使用Sequence类可以方 ...

  3. thinkphp5使用前置后置操作

    下面举个例子,前置删除的例子   模型事件只可以在调用模型的方法才能生效,使用查询构造器通过Db类操作是无效的   控制器中实例化类   $cate=model('cate'); $cate-> ...

  4. Java中的"\t"

    \t相当于表格制表符tab键,一个格内放8的整数倍的字符,根据显示的字符串长度,剩下的显示空格数.比如:字符串长度为1,那么距离下一个串的空格数应该是8-1=7:如果字符串长度为2,那么距离下一个串的 ...

  5. 洛谷 P3676 小清新数据结构题

    https://www.luogu.org/problemnew/show/P3676 这题被我当成动态dp去做了,码了4k,搞了一个换根的动态dp #include<cstdio> #i ...

  6. java.lang.ClassNotFoundException: org.slf4j.LoggerFactory

    缺少slf4j-api.jar和slf4j-log4j12.jar这两个jar包导致的错误.

  7. C. Epidemic in Monstropolis

    http://codeforces.com/contest/733/problem/C 一道很恶心的模拟题. 注意到如果能凑成b[1],那么a的前缀和一定是有一个满足是b[1]的,因为,如果跳过了一些 ...

  8. Java thymeleaf模板获取资源文件的内容

    我们在某些时候可能需要获取配置文件properties中的配置信息,而不需要用Java传给模板,在模板中就可以直接获取 我们需要在resources/下定义国际化配置文件即可,注意名称必须中messa ...

  9. js 回车提交表单

    一.整个页面用一个回车提交事件: <input type="button" value="回车提交" id="auto" onclic ...

  10. 电脑Bois中usb模式启动热键

    组装机主板 品牌笔记本 品牌台式机 主板品牌 启动按键 笔记本品牌 启动按键 台式机品牌 启动按键 华硕主板 F8 联想笔记本 F12 联想台式机 F12 技嘉主板 F12 宏基笔记本 F12 惠普台 ...