您的位置:首页 > 其它

洛谷OJ P1379 八数码难题 解题报告

2015-05-16 23:41 211 查看

洛谷OJ P1379 八数码难题 解题报告

by MedalPluS

题目描述

在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

输入格式

输入初试状态,一行九个数字,空格用0表示

输出格式

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

分析

其实就是个裸的BFS,但是可以拓展很多知识点(有人说不做这题的人的OI人生不完美)

暴力BFS的话会被卡,因为要搜索的状态实在太多了

所以这里有两种优化:

1.启发式搜索

我们假设f=g+h,那么g可以设为已经挪动的步数,h为不在位将牌个数,这样就构建了一个估价函数

然后直接写就好

2.双向BFS

我们知道起点和终点,直接双向就好

另外,这里应该如何判重呢?其实很简单,下面引入康托展开:/article/1461578.html

然后就利用康托值进行判重就好

代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstdlib>
using namespace std;

#define rep(i,l,r) for(i=l;i<=r;i++)
const int maxn=10;
const int maxm=1000001;//9!*8+8!*7...<=5000001

struct node{
int a[maxn][maxn];
short int g;
node(){g=0;}
}Head,Tail;

short int vis_f[maxm],vis_b[maxm];
int tmps[maxn];
queue<node> f,b;

int fact(int x){
int ans;
for(ans=1;x>=2;x--)ans*=x;
return ans;
}

int Cantor(node now){//Cantor_Expand
int i,j,t=0,res=0;
rep(i,1,3)
rep(j,1,3)
tmps[t++]=now.a[i][j];
t=0;
rep(i,0,8)
{
t=0;
rep(j,i+1,8)
if(tmps[j]<tmps[i])
t++;
res+=t*fact(9-i-1);
}
return res;
}

void init_Front(){
Head.a[1][1]=1;Head.a[1][2]=2;Head.a[1][3]=3;
Head.a[2][1]=8;Head.a[2][2]=0;Head.a[2][3]=4;
Head.a[3][1]=7;Head.a[3][2]=6;Head.a[3][3]=5;
Head.g=0;
}

void init_Back(){
int i,j;
rep(i,1,3)
rep(j,1,3)
scanf("%1d",&Tail.a[i][j]);
}

void init(){
memset(vis_f,-1,sizeof(vis_f));
memset(vis_b,-1,sizeof(vis_b));
init_Front();
init_Back();
}

void expand_f(){
node head,tmp;
head=f.front();
f.pop();
int i,j,sx,sy;
rep(i,1,3)
rep(j,1,3)
if(!head.a[i][j])
{
sx=i;
sy=j;
break;
}
tmp=head;
if(sx-1>0){
swap(tmp.a[sx-1][sy],tmp.a[sx][sy]);
tmp.g++;
if(vis_b[Cantor(tmp)]!=-1){cout<<tmp.g+vis_b[Cantor(tmp)];exit(0);}
if(vis_f[Cantor(tmp)]==-1){
vis_f[Cantor(tmp)]=tmp.g;
f.push(tmp);
}
}
tmp=head;
if(sx+1<=3){
swap(tmp.a[sx+1][sy],tmp.a[sx][sy]);
tmp.g++;
if(vis_b[Cantor(tmp)]!=-1){cout<<tmp.g+vis_b[Cantor(tmp)];exit(0);}
if(vis_f[Cantor(tmp)]==-1){
vis_f[Cantor(tmp)]=tmp.g;
f.push(tmp);
}
}
tmp=head;
if(sy-1>0){
swap(tmp.a[sx][sy-1],tmp.a[sx][sy]);
tmp.g++;
if(vis_b[Cantor(tmp)]!=-1){cout<<tmp.g+vis_b[Cantor(tmp)];exit(0);}
if(vis_f[Cantor(tmp)]==-1){
vis_f[Cantor(tmp)]=tmp.g;
f.push(tmp);
}
}
tmp=head;
if(sy+1<=3){
swap(tmp.a[sx][sy+1],tmp.a[sx][sy]);
tmp.g++;
if(vis_b[Cantor(tmp)]!=-1){cout<<tmp.g+vis_b[Cantor(tmp)];exit(0);}
if(vis_f[Cantor(tmp)]==-1){
vis_f[Cantor(tmp)]=tmp.g;
f.push(tmp);
}
}
}

void expand_b(){
node head,tmp;
head=b.front();
b.pop();
int i,j,sx,sy;
rep(i,1,3)
rep(j,1,3)
if(!head.a[i][j])
{
sx=i;
sy=j;
break;
}
tmp=head;
if(sx-1>0){
swap(tmp.a[sx-1][sy],tmp.a[sx][sy]);
tmp.g++;
if(vis_f[Cantor(tmp)]!=-1){cout<<tmp.g+vis_f[Cantor(tmp)];exit(0);}
if(vis_b[Cantor(tmp)]==-1){
vis_b[Cantor(tmp)]=tmp.g;
b.push(tmp);
}
}
tmp=head;
if(sx+1<=3){
swap(tmp.a[sx+1][sy],tmp.a[sx][sy]);
tmp.g++;
if(vis_f[Cantor(tmp)]!=-1){cout<<tmp.g+vis_f[Cantor(tmp)];exit(0);}
if(vis_b[Cantor(tmp)]==-1){
vis_b[Cantor(tmp)]=tmp.g;
b.push(tmp);
}
}
tmp=head;
if(sy-1>0){
swap(tmp.a[sx][sy-1],tmp.a[sx][sy]);
tmp.g++;
if(vis_f[Cantor(tmp)]!=-1){cout<<tmp.g+vis_f[Cantor(tmp)];exit(0);}
if(vis_b[Cantor(tmp)]==-1){
vis_b[Cantor(tmp)]=tmp.g;
b.push(tmp);
}
}
tmp=head;
if(sy+1<=3){
swap(tmp.a[sx][sy+1],tmp.a[sx][sy]);
tmp.g++;
if(vis_f[Cantor(tmp)]!=-1){cout<<tmp.g+vis_f[Cantor(tmp)];exit(0);}
if(vis_b[Cantor(tmp)]==-1){
vis_b[Cantor(tmp)]=tmp.g;
b.push(tmp);
}
}
}

void double_BFS(){
int len1,len2;
f.push(Head);b.push(Tail);
vis_f[Cantor(Head)]=vis_b[Cantor(Tail)]=0;
while(!f.empty() || !b.empty()){
len1=f.size();len2=b.size();
if(len1>len2){
if(len2)expand_b();
expand_f();
}
else {
if(len1)expand_f();
expand_b();
}
}
}

int main(){
init();
double_BFS();
return 0;
}


代码量比较大,竟然有个小200....
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: