问题 C: 【宽搜入门】8数码难题

时间限制: 20 Sec  内存限制: 128 MB
提交: 193  解决: 69
[提交][状态][讨论版][命题人:外部导入]

题目描述

初始状态的步数就算1,哈哈

输入:第一个3*3的矩阵是原始状态,第二个3*3的矩阵是目标状态。
输出:移动所用最少的步数

Input

2 8 3
1 6 4
7 0 5
1 2 3
8 0 4
7 6 5

Output

6

[提交][状态]

解题思路

  • 本题是一个有限状态-有限行动集的离散马尔科夫决策过程(多阶段决策过程)。棋盘的当前布局为当前的状态State,我们可以有4种动作Action(即四种状态转移方式),转移到下一状态。每一个状态就是一个结点
  • 这种状态状态转移过程,可具有图结构的描述形式,则 进一步可以才用图遍历的几种方法:BFS,DFS。对于这种最优问题,因为问题中状态的有限性,我们完全可以采用穷举法直接暴力搜索。离散问题,很经常 状态集是有限可枚举的,直接遍历就好了,很难在数学上刻画性质,刻画最优点的解析形式(离散问题 很多都难有解析方法,必须遍历)。
  • 这时候采用图遍历,容易内存超限,要注意剪枝,能有效降低空间复杂度。但要注意,剪枝方法本身可能带来 过多的内存消耗,剪枝方法 要注意选取、权衡。
  • BFS剪枝的两种方法。思想:降低入队结点的重复率。
  1. 去除全部重复入队结点,将入队结点的重复率降至0。保存入队信息的数据结构方案有:1. set(动态,红黑树)  2.数组table(静态)。 属于最极端的方法是:曾入队的结点(状态)无需再入队。结点之间的关系(前驱后继关系) 不刻画也没关系。这种方法要求较多的内存 记录每个状态的入队信息。然而我们剪枝的目的就是要减少遍历的数量,进而减少内存消耗。故该方法 有点矛盾。不过选择合适的数据结构方案可减缓该矛盾。
  2. 去除部分重复入队结点,一定程度上降低入队结点的重复率。这需要一定的技巧,因为要确定去除哪些高频重复结点好。由上一方法,引发思考,我们可以只记录部分状态的入队信息,进而减少内存消耗。本题中就只记录上上个状态的入队信息,避免消耗过多的内存来记录所有状态的入队信息。但会一定程度上增加 结点的重复入队率。在本题中,采用这种剪枝方法 降低了总体的内存消耗。
  3. 本题代码实测性能对比图。其中方法一:去除全部重复入队结点。方法二:去除 与上上个结点重复的入队结点。效果对比:方法二 在内存消耗上 效果提升了。因为方法一采用map 来记录入队信息,耗时还增加了。

 

AC代码

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<string>
#include<map>
using namespace std;
// char Table[400000000]={0};
map<string, int> mp;
const int maxn=3;
const int dx[]={0,-1,0,1};
const int dy[]={-1,0,1,0};
struct node{
    int x, y;
    int a[maxn][maxn];
    int step;
    int preX, preY;
};
void copyM(int a[][maxn], int b[][maxn]){
    for(int i=0; i<3; i++){
        for(int j=0; j<3; j++){
            a[i][j] = b[i][j];
        }
    }   
}
//散列映射,a[]映射为string
string hashM(int a[][maxn]){
    string str;
    for(int i=0; i<3; i++){
        for(int j=0; j<3; j++){
            str.push_back(a[i][j]+'0');
        }
    }
    return str;
}
int findM(int a[][maxn]){
    
}
int equalM(int a[][maxn], int b[][maxn]){
    for(int i=0; i<3; i++){
        for(int j=0; j<3; j++){
            if(a[i][j] != b[i][j]) return 0;
        }
    }
    return 1;
}
void printM(int a[][maxn]){
    for(int i=0; i<3; i++){
        for(int j=0; j<3; j++){
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
}
int BFS(node s, int b[][maxn]){
    queue<node> q;
    q.push(s);
    do{
        node now = q.front();
        q.pop();
        if(equalM(now.a, b)) return now.step;
        for(int i=0; i<4; i++){
            //构造,搜索下一结点
            node next = now;
            next.x = now.x + dx[i];
            next.y = now.y + dy[i];
            if(next.x>2 || next.x<0 || next.y>2 || next.y<0) continue;
            if(next.x == now.preX && next.y == now.preY) continue;
            swap(next.a[now.x][now.y], next.a[next.x][next.y]);
            next.step++;
            next.preX=now.x;
            next.preY=now.y;
            //判断结点是否入队过。用Table记录结点的入队信息
            q.push(next);
            //mp[hashM(next.a)]=1;
            // if(mp.find(hashM(next.a)) == mp.end()) {
            //     q.push(next);
            //     mp[hashM(next.a)]=1;
            // }
            // printf("%d\n", hashM(next.a));
            // printM(next.a);
        }
    } while(!q.empty());
    return -1;
}
int main(){
    int b[maxn][maxn]={0};
    node s;
    int x, y;
    for(int i=0; i<3; i++){
        for(int j=0; j<3; j++){
            scanf("%d", &s.a[i][j]);
            if(s.a[i][j] == 0) s.x=i, s.y=j, s.step=1;
        }
    }
    for(int i=0; i<3; i++){
        for(int j=0; j<3; j++){
            scanf("%d", &b[i][j]);
        }
    }
    // printM(s.a);
    // printM(b);
    printf("%d\n", BFS(s, b));
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值