八数码问题
图片来自于教材《算法竞赛入门到进阶》罗勇军 郭卫斌 著
从这里就能看出,判重(判断某一个状态是否已经访问过)是相当必要的,否则就会耗费大量的时间,所以如何记录没种状态就是一个重要的问题,书上介绍了康托展开的方法。
康托展开
例题
AC代码
#include <iostream>
#include <queue>
#include <string.h>
#include <algorithm>
#include <vector>
using namespace std;
int fact[10] = {1,1,2,6,24,120,720,5040,40320,362880};//fact[i]表示i的阶乘,提前计算,否则会超时
int vis[362880+5];//用于记录状态是否已被访问过
int head[9];//初始状态
int obj[9] = {1,2,3,8,0,4,7,6,5};//目标状态
int dir[4][2] = {{-1,0}, {0,-1}, {1,0}, {0,1}};//左上右下
struct State{
int str[9];//状态
int step;//从初始状态到达该状态走过的步数
};
bool Cantor(int a[], int n){//检测a[]状态是否被访问过,若被访问过则返回false,没有被访问过返回true并且置vis位为1
long result = 0;
for(int i=0; i<n; i++){
int counted = 0;//a[i]排在第几位
for(int j=i+1; j<n; j++){
if(a[i] > a[j]){
counted++;
}
}
result += 1LL * counted * fact[n-1-i];
}
if(!vis[result]){
vis[result] = 1;
return true;
}
else
return false;
}
int BFS(){
queue<State> q;
State ps;
memcpy(ps.str, head, sizeof(ps.str));
ps.step = 0;
q.push(ps);
Cantor(head, 9);
while(!q.empty()){
ps = q.front();
q.pop();
if(memcmp(ps.str, obj, sizeof(ps.str)) == 0){
return ps.step;
}
for(int i=0; i<4; i++){//四个方向
int t=0;
while(ps.str[t]) t++; //判断0的位置
int x,y;
x = t%3;
y = t/3;//求0的二维坐标
int xt,yt;
xt = x + dir[i][0];
yt = y + dir[i][1]; //求下一个位置的二维坐标
if(xt>=0 && xt<3 && yt>=0 && yt<3){
State news = ps;
int j = yt*3+xt;
swap(news.str[t], news.str[j]);
if(Cantor(news.str,9)){
news.step++;
q.push(news);
}
}
}
}
return -1;
}
int main(int argc, char *argv[]) {
/*
//提前计算1到9的阶乘
int res = 1;
for(int i=1; i<=9; i++){//从1算到9的阶乘
res *= i;
cout<<res<<',';
}
*/
string s;
cin>>s;
for(int i=0; i<=8; i++){
head[i] = s[i]-'0';
}
cout<<BFS()<<endl;
return 0;
}
遇到的问题:
一开始我把所有的二维坐标都看作一维坐标,结果出错了,出错地方:
0 1 2
3 4 5
6 7 8
例如2的位置右边应该是没有的,我按一维坐标看,直接2+1得3,就发生了错误。