| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 684 人关注过本帖
标题:八数码问题 恍然大悟
取消只看楼主 加入收藏
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
结帖率:95.65%
收藏
已结贴  问题点数:20 回复次数:2 
八数码问题 恍然大悟

题目是这样的:
     所谓八数码问题是指这样一种游戏:将分别标有数字1,2,3,…,8的八块正方形数码牌任意地放在一块3×3的数码盘上。放牌时要求不能重叠。于是,在3×3的数码盘上出现了一个空格。现在要求按照每次只能将与空格相邻的数码牌与空格交换的原则,将任意摆放的数码盘逐步摆成某种特殊的排列。


关于八数码的问题,如果有更好的思路。欢迎写上,分享分享。

想起昨天晚上还在问B版这问题,但是B版大神太忙了,没回,幸好没回,不然我都怀疑我的智商了~~
刚要睡觉,拿起一看,哎呀尼玛,对不起,爆出口了,这题不就和倒水问题本质差不多么,突然恍然大悟,突然才知道这就是所谓的图的最短路,但不允许往回重复(这样表达可能有歧义),也就是B版说的无环图,加上搜素策略的定向性,其实写代码时也就转化为树的模型了。


具体思路:
    用广度优先搜索枚举各个合法状态。用队列拓展之。每出队列,就判断是否是目标状态,是找到最少移动步骤解,输出最少移动步骤,退出搜素。


激动啊~~,明晚贴代码。
恳请各位大神指教一二。
搜索更多相关主题的帖子: 数码 正方形 
2013-03-17 01:42
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
收藏
得分:0 
程序代码:
#include <stdio.h>
#include <string.h>
typedef int State[9];//状态类型 存储八数码 
State queue[100000];//队列 
int step[100000];//对应结点到根结点的距离,即步骤 
const int dx[] = {0,0,1,-1};//定义方向 
const int dy[] = {-1,1,0,0};
int front,rear;
const int MAXHASHSIZE = 1000003;
int head[MAXHASHSIZE],next[MAXHASHSIZE];

int vis[36288],fact[9];
int bfs(State &r,State & g);
int main(){
    State root,goal;
    while(1){
        for(int i = 0; i < 9; i++)
            scanf("%d",&root[i]);
        for(int i = 0; i < 9; i++)
            scanf("%d",&goal[i]);
        int ans = bfs(root,goal);
        if(ans > 0)
            printf("%d ",step[ans]);
        else
            printf("-1\n");
    }
}
int bfs(State & r,State &g){
    init_lookup_table();
    memset(step,0,sizeof(step));//初始化所有状态对应步骤 
    front = rear = 0;
    memcpy(queue[rear++],r,sizeof(r));//跟结点入队列 
    while(front < rear){ 
        State v;
        memcpy(v,queue[front++],sizeof(v));//出队列
        if(memcmp(v,g,sizeof(g)) == 0) 
            return front - 1;
        int z;
        for(z = 0 ; z < 9 ; z++)
            if(!v[z])
                break;
        int xx = z / 3;              //行号 
        int yy = z % 3;              //列号 
        for(int d = 0 ; d < 4 ; d++){//扫描四个方向 
            int newx = xx + dx[d];//新方向 
            int newy = yy + dy[d]; 
            if(newx >=0 && newx <= 2 && newy >=0 && newy <= 2){//如果该方向合法,则入队列 
                int k = 3 * newx + newy; //记录下这个方向的编号 
                State u;
                memcpy(u,v,sizeof(v)); 
                u[z] = u[k];// 实现移动 
                u[k] = 0;
                memcpy(queue[rear],u,sizeof(u));//入队列 
                step[rear - 1] = step[front - 1] + 1;//更新对应结点的所对应步骤 
                if(try_to_insert(rear))
                    rear++; 
            } 
        }
    }
}


这段代码不是完全自己写的,而且还有个要借助hash函数的功能没完成,写的时候有参考LRJ的算法书籍。改天学习下hash算法再补充完整,先结贴了。

再复杂的问题也基于最简单的原理。耐心,耐心!丰富自己!等待时机!
2013-03-17 23:57
fourleaves
Rank: 2
等 级:论坛游民
帖 子:194
专家分:99
注 册:2010-5-8
收藏
得分:0 
程序代码:
#include <stdio.h>
#include <string.h>
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};
const int MAXHASHSIZE = 1000003;
int head[MAXHASHSIZE];
int next[MAXSTATE];
void init_lookup_table(){
    memset(head,0,sizeof(head));
}
int hash(State &v){
    int j = 0;
    for(int i = 0; i < 9; i++){//除留余数法算hash
        j = j * 10 + v[i];
    }
    return j % MAXHASHSIZE;
}

int try_insert(int s){//相当于静态链表
    int h = hash(st[s]);//获取st[s]状态的hash值
    int u = head[h];//获取哈下表映射上存的链表首结点
    while(u){// u == 0 表示为链表为空或者指针指向NULL
        
        if(memcmp(st[u],st[s],sizeof(st[s])) == 0)//要插入的状态已经存在
            return 0;//返回0表示错误,即防止其往回走,这是为了去掉图中的环
        u = next[u];     
    } 
    next[s] = head[h];
    head[h] = s;
    return 1;
}

int bfs(){
    init_lookup_table();
    int front = 1, rear = 2;//这里之所以不使用下标零,是因为在上面的链表中,把0视为不存在,即链表中的NULL 
    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;//行号 
        int 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(try_insert(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 = bfs();
    if(ans > 0)
        printf("%d\n",dist[ans]);
    else
        printf("-1\n");
    return 0;
} 


没几个人看~~唉,这代码有大部分参考了LRJ大神的,不过把注释补得全点了,有兴趣的看看吧。

再复杂的问题也基于最简单的原理。耐心,耐心!丰富自己!等待时机!
2013-03-18 23:59
快速回复:八数码问题 恍然大悟
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.022529 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved