cdoj 414 八数码 (双向bfs+康拓展开,A*)
一道关乎人生完整的问题。
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*)的更多相关文章
- 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 ...
- 8数码,欺我太甚!<bfs+康拓展开>
不多述,直接上代码,至于康拓展开,以前的文章里有 #include<iostream> #include<cstdio> #include<queue> using ...
- hdu3567 八数码2(康托展开+多次bfs+预处理)
Eight II Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 130000/65536 K (Java/Others)Total S ...
- 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 ...
- HDU 1043 Eight(双向BFS+康托展开)
http://acm.hdu.edu.cn/showproblem.php?pid=1043 题意:给出一个八数码,求出到达指定状态的路径. 思路:路径寻找问题.在这道题里用到的知识点挺多的.第一次用 ...
- poj 1077 Eight (八数码问题——A*+cantor展开+奇偶剪枝)
题目来源: http://poj.org/problem?id=1077 题目大意: 给你一个由1到8和x组成的3*3矩阵,x每次可以上下左右四个方向交换.求一条路径,得到12345678x这样的矩阵 ...
- [cdoj1380] Xiper的奇妙历险(3) (八数码问题 bfs + 预处理)
快要NOIP 2016 了,现在已经停课集训了.计划用10天来复习以前学习过的所有内容.首先就是搜索. 八数码是一道很经典的搜索题,普通的bfs就可求出.为了优化效率,我曾经用过康托展开来优化空间,甚 ...
- 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 ...
- HDU 4531 bfs/康拓展开
题目链接http://acm.hdu.edu.cn/showproblem.php?pid=4531 吉哥系列故事——乾坤大挪移 Time Limit: 2000/1000 MS (Java/Othe ...
随机推荐
- 对于BeanUtils类的方法populate的一些个人拙见
今天在做一个登录案列,期间涉及到了BeanUtils类,这个类也是第一次涉及到,视频没有细讲.只能自己摸着石头过河.慢慢思考.下面来说一下自己个人对这个类的方法populate的理解 这个类应该不是单 ...
- PAT甲级——1107 Social Clusters (并查集)
本文同步发布在CSDN:https://blog.csdn.net/weixin_44385565/article/details/90409731 1107 Social Clusters (30 ...
- mysql5.7安装部署后初始密码查看以及修改
一.查看初始密码以下两种方法: 1.找到自己的error.log日志文件,执行自己的命令,红色标记的部分为初始化密码. grep 'temporary password' /data/mysql/er ...
- 学习flask的网址
学习flask的网址: http://www.bjhee.com
- 30道python真实面试题(搜集到的,看看其实都是基础)
1.一行代码实现1-100之间和 In [1]: sum(range(0,101)) Out[1]: 5050 2.如何在一个函数内部修改全局变量 利用global修改全局变量 In [2]: a = ...
- ASP.NET Core性能测试
ASP.NET Core性能测试 应用性能直接影响到托管服务的成本,因此公司在开发应用时需要格外注意应用所使用的Web框架,初创公司尤其如此.此外,糟糕的应用性能也会影响到用户体验,甚至会因此受到相关 ...
- Python 踩坑之旅进程篇其四一次性踩透 uid euid suid gid egid sgid的坑坑洼洼
目录 1.1 踩坑案例 1.2 填坑解法 1.3 坑位分析 1.4 技术关键字 1.5 坑后思考 下期坑位预告 代码示例支持 平台: Centos 6.3 Python: 2.7.14 代码示例: 菜 ...
- CentOS7.2配置本地yum源
1.检查是否有本地yum源 1)检查是否能连网 ping www.baidu.com 2)检查是否有本地yum源 yum list 2.挂载镜像文件 以上检查,说明确实是内网,也确实没有本地yum源, ...
- SpriingMVC执行流程结构
SpringMVC也叫spring web mvc,属于表现层的框架,是Spring框架的一部分. Spring MVC请求流程图: request-------->DispatcherSer ...
- Web.config文件 详解
一.认识Web.config文件Web.config 文件是一个XML文本文件,它用来储存 ASP.NET Web 应用程序的配置信息(如最常用的设置ASP.NET Web 应用程序的身份验证方式), ...