时间限制: 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剪枝的两种方法。思想:降低入队结点的重复率。
- 去除全部重复入队结点,将入队结点的重复率降至0。保存入队信息的数据结构方案有:1. set(动态,红黑树) 2.数组table(静态)。 属于最极端的方法是:曾入队的结点(状态)无需再入队。结点之间的关系(前驱后继关系) 不刻画也没关系。这种方法要求较多的内存 记录每个状态的入队信息。然而我们剪枝的目的就是要减少遍历的数量,进而减少内存消耗。故该方法 有点矛盾。不过选择合适的数据结构方案可减缓该矛盾。
- 去除部分重复入队结点,一定程度上降低入队结点的重复率。这需要一定的技巧,因为要确定去除哪些高频重复结点好。由上一方法,引发思考,我们可以只记录部分状态的入队信息,进而减少内存消耗。本题中就只记录上上个状态的入队信息,避免消耗过多的内存来记录所有状态的入队信息。但会一定程度上增加 结点的重复入队率。在本题中,采用这种剪枝方法 降低了总体的内存消耗。
- 本题代码实测性能对比图。其中方法一:去除全部重复入队结点。方法二:去除 与上上个结点重复的入队结点。效果对比:方法二 在内存消耗上 效果提升了。因为方法一采用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;
}