路径寻找问题可以归结为隐式图的遍历,他的任务是找一条从初始状态到终止状态的最优路径,回溯法是找到一些符合要求的解
八数码问题
分析:
把八数码问题归结为图上最短路问题,用bfs求解
#include<stdio.h>
#include<cstring>
using namespace std;
typedef int State[9];
const int maxstate=1000000;
State st[maxstate],goal; //状态数组,所有的数组都保存在这里
int dist[maxstate]; //距离数组
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
//bfs,返回目标状态在st数组下标
int bfs(){
init_lookup_table(); //初始化查找表
int front=1,rear=2; //不使用下标0,因为0被看做不存在
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; //找“0”的位置
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&&newx>=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(try_to_insert(rear))
rear++; //如果成功插入查找表,修改队尾指针
}
}
front++;
}
return 0;
}
//用到了cstring中的memcmp和memcpy完成整块内存的比较与复制
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=bfs();
if(ans>0)
printf("%d\n",dist[ans]);
else
printf("-1\n");
return 0;
}
接下解决判重问题,下面介绍三种方法
方法一:设计一套编码与解码的函数(这里还不是很明白)
int vis[362880],fact[9];
void init_lookup_table()
{
fact[0]=1;
for(int i=1;i<9;i++)
fact[i]=fact[i-1]*i;
}
int try_to_insert(int s){
int code=0;
for(int i=0;i<9;i++){
int cnt=0;
for(int j=i+1;j<9;j++)
if(st[s][j]<st[s][i]) cnt++;
code+=fact[8-i]*cnt;
}
if(vis[code])
return 0;
return vis[code]=1;
}
效率虽高,适用范围不大
方法二:使用哈希技术,
const int hashsize=1000003;
int head[hashsize],next[maxstate];
void init_lookup_table() {memset(head,0,sizeof(head));}
int hash(State& s){
int v=0;
for(int i=0;i<9;i++) v=v*10+s[i]; //把9个数字组合成9位数
return v%hashsize;
}
int try_to_insert(int s){
int h=hash(st[s]);
int u=head[h]; //从表头开始查找链表
while(u){
if(memcmp(st[u],st[s],sizeof(st[s]))==0)
return 0;
u=next[u];
}
next[s]=head[h]; //插入到链表中
head[h]=s;
return 1;
}
方法三:用STL集合
set<int>vis;
void init_lookup_table()
{
vis.clear();
}
int try_to_insert(int s)
{
int v=0;
for(int i=0;i<9;i++)
v=v*10+st[s][i];
if(vis.count(v))
return 0;
vis.insert(v);
return 1;
}