论久违的八数码问题——小试牛刀
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了论久违的八数码问题——小试牛刀相关的知识,希望对你有一定的参考价值。
八数码问题
八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每一个棋子上标有1至8的某一数字,不同棋子上标的数字不同样。棋盘上另一个空格,与空格相邻的棋子能够移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。——摘自DALAO笔记
{http://www.tuicool.com/articles/QnQ7Z3}
身为一个蒟蒻,能想到的第一个方法就是BFS。BFS在这道题里可谓是如鱼得水,边界清晰加上数据范围小,直接暴力也不是不无可能,只需要判重,避免同一节点访问多次即可。当然,判重可以有不同的方法,比如STL中的SET,HASH,以及简单判重。由于STL使用简单快捷,所以蒟蒻就写了个set判重的函数。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<set> using namespace std; typedef int state[9]; const int maxs=1000000; state st[maxs],goal; int dist[maxs]; const int dx[]={-1,1,0,0}; const int dy[]={0,0,-1,1}; set<int> vis; void lookup() { vis.clear(); } int tangguoqiang(int s) { int v=0; for(int i=1;i<=9;i++) { v=v*10+st[s][i]; } if(vis.count(v)) return 0; vis.insert(v); return 1; } int hehe() { lookup(); int front=1,rear=2; while(front<rear) { state& s=st[front]; if(memcmp(goal,s,sizeof(s))==0) return front; int z; for(z=0;z<9;z++) if(!s[z]) break; int x=z/3,y=z%3; for(int d=0;d<4;d++) { int newx=x+dx[d]; int newy=y+dy[d]; int newz=newx*3+newy; if(newx>=0&&newx<3&&newy>=0&&newy<3) { state& t=st[rear]; memcpy(&t,&s,sizeof(s)); t[newz]=s[z]; t[z]=s[newz]; dist[rear]=dist[front]+1; if(tangguoqiang(rear)) rear++; } } front++; } return 0; } int main() { for( int i=0;i<9;i++) { scanf("%d",&st[1][i]); } for(int i=0;i<9;i++) { scanf("%d",&goal[i]); } int ans=hehe(); if(ans>0) printf("%d\n",dist[ans]); else printf("-1\n"); return 0; }
在时间上可以用memcmp和memcpy来比较整个内存和复制整个内存,相比于循环复制快了许多。(函数在cstring中)。
关于第一个函数,起到了一个初始化的作用,便于管理;第二个函数(唐国强什么的都是乱取名)用来判重,set里是自动排序所以非常方便。
//由于我没有随手写注释的习惯,可能代码阅读起来有些困难,dalao和jvruo不要打我啊呀呀………………
以上是关于论久违的八数码问题——小试牛刀的主要内容,如果未能解决你的问题,请参考以下文章
不带任何优化且使用了巨慢STL容器set来查重的随便在哪个OJ上提交都会TLE的八数码(基本是从刘汝佳抄的)