7.5路径寻找问题

路径寻找问题可以归结为隐式图的遍历,他的任务是找一条从初始状态到终止状态的最优路径,回溯法是找到一些符合要求的解

八数码问题

分析:

把八数码问题归结为图上最短路问题,用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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值