洛谷Oj-P1379 八数码难题-广搜+康拓展开

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

struct state
{
    string s;//状态
    int x;//0的坐标
    int y;
    int cnt;//花费的操作数
};
const int f[] = {40320,5040,720,120,24,6,2,1,1};//倒序阶乘表,8!,7!……,0!
int g[4][4];//存图
bool book[400010];//标记数组,对状态进行标记
inline int cantor(string s)//康托展开
{
    int ans = 0;
    for(int i = 0; i <= 8; ++i)
    {
        int cnt = 0;
        for(int j = i + 1; j <= 8; ++j)
            if(s[i] > s[j])
                cnt++;
        ans += cnt * f[i];
    }
    return ans;
}
inline string trans(string s,int x,int y,int i)//状态的转移
{
    int id = 3 * (x - 1) + (y - 1);//计算出0在字符串中的下标
    if(i == 0)//上
        swap(s[id],s[id - 3]);
    else if(i == 1)//下
        swap(s[id],s[id + 3]);
    else if(i == 2)//左
        swap(s[id],s[id - 1]);
    else//右
        swap(s[id],s[id + 1]);
    return s;
}
bool bfs(state start)
{
    queue<state> q;
    q.push(start);//将起始状态入队
    book[cantor(start.s)] = true;//标记
    while(!q.empty())
    {
        state t = q.front();//取出状态t
        q.pop();
        for(int i = 0; i <= 3; ++i)
        {
            int tx = t.x + dirx[i];
            int ty = t.y + diry[i];
            if(tx < 1 || tx > 3 || ty < 1 ||ty > 3)//越界
                continue;
            string next_s = trans(t.s,t.x,t.y,i);//状态转移
            int Hash = cantor(next_s);//计算哈希值
            if(Hash == 46685)//如果到达了终点
            {
                cout << t.cnt + 1 << endl;//输出答案
                return true;
            }
            if(book[Hash] == false)//如果没被标记过
            {
                q.push({next_s,tx,ty,t.cnt + 1});//入队
                book[Hash] = true;//标记
            }
        }
    }
    return false;
}
int main()
{
    string start;//起始状态
    int x,y;//0的坐标
    for(int i = 1; i <= 3; ++i)
    {
        for(int j = 1; j <= 3; ++j)
        {
            scanf("%1d",&g[i][j]);//以矩阵形式保存
            start += g[i][j] + '0';
            if(g[i][j] == 0)//找出0的下标
            {
                x = i;
                y = j;
            }
        }
    }
    if(cantor(start) == 46685)//很重要,特判初始时就是目标状态的情况
    {
        cout << 0 << endl;
        return 0;
    }
    if(bfs({start,x,y,0}) == false)//如果失败
        cout << "cannot" << endl;
    return 0;
}

解决方法:
不要忘记特判!
本题搜索思路很简单,对状态的标记也就是如何判重是个难点
利用康拓展开将数串映射为一个不大的整数,从而很方便地判重
字串和矩阵直接的关系也要在纸上演算

阅读更多
版权声明:欢迎转载 https://blog.csdn.net/suntengnb/article/details/79945552
个人分类: 广度优先搜索
想对作者说点什么? 我来说一句

康拓展开八数码广搜程序

2012年11月28日 3KB 下载

没有更多推荐了,返回首页

不良信息举报

洛谷Oj-P1379 八数码难题-广搜+康拓展开

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭