#include<iostream>
#include<vector>
#include<string>
#include<queue>
using namespace std;
int fac[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 };//康拖展开判重// 0!1!2!3! 4! 5! 6! 7! 8! 9!
int moves[4][2] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };//u,d,l,r
char moveIndex[5] = "udlr";//正向搜索
int index[1000000] = {false};
typedef struct point
{
int s[9];//为什么不选择二维数组呢?1、数组规模已知,一维可以转化为二维 2、二维遍历比较麻烦
int number;//康托展开hash值 原因:1、映射到一个数字,方便比较 2、方便标记是否被访问过
int loc;//0的位置
int hx;
int step;
string path;
void gethx(point aim)
{
hx = 0;
for (int i = 0; i < 9; i++)
{
int x = i / 3, y = i % 3;
for (int j = 0; j < 9; j++)
{
if (s[i] == aim.s[j])
{
int aimx = j / 3, aimy = j % 3;
hx = abs(aimx - x) + abs(aimy - y);
}
}
}
}
void sToNumber()//结构体内已经定义的变量可直接引用。那么这结构体和类的区别有哪些。构造函数???
{
number = 0;//在初始化是叠加了当然会导致错误智障!
for (int i = 0; i < 9; i++)
{
int count = 0;
for (int j = i + 1; j < 9; j++) if (s[j] < s[i]) count++;
number += (fac[9 - i - 1] * count);
}
}
};
struct cmp{//重载括号
bool operator()(point a, point b)
{
return (a.hx+a.step)>(b.hx+b.step);
}
};
bool dfs(point &in, point &aim,string &path)
{
priority_queue<point,vector<point>,cmp> p;//找最小,两种方式:1、最小堆 2、优先队列 //不能不写中间那项,默认参数的规则
in.gethx(aim);
p.push(in);
index[in.number] = true;
while (!p.empty()){
cout << 1;
point cur = p.top();
p.pop();
if (cur.number==aim.number){
path = cur.path;
return true;
}
//没找到,准备交换入栈
int x = cur.loc / 3, y = cur.loc % 3;
//cout << x << " " << y << endl;
for (int i = 0; i < 4; i++){
int xtemp=x+(moves[i][0]); int ytemp=y+(moves[i][1]);//0的新位置
if (x<0 || x>2 || y<0 || y>2) continue;
point next(cur);
next.loc = xtemp * 3 + ytemp;
//cout << next.loc;
swap(next.s[next.loc], next.s[cur.loc]);
//for (int i = 0; i < 9; i++) cout << next.s[i] << " ";
next.sToNumber();
if (index[next.number] == true) continue;
next.step++;
next.path = cur.path + moveIndex[i];
next.gethx(aim);
index[next.number] = true;
if (next.number == aim.number)
{
cout << "最少步数为:" << next.step << endl;
path = next.path;
return true;
}
//cout << next.number << " " << aim.number << endl;
p.push(next);
}
//system("pause");
}
return false;
}
int main()
{
point in, aim;
//cout << "请输入起始位置";
int temp1[9] = { 2,3,4,1,5,0,7,6,8 };
int temp2[9] = { 1,2,3,4,5,6,7,8,0 };
for (int i = 0; i < 9; i++){
in.s[i] = temp1[i];
if (in.s[i] == 0) in.loc = i;
}
in.step = 0;
in.sToNumber();
for (int i = 0; i < 9; i++)
{
aim.s[i]=temp2[i];
}
aim.sToNumber();
string path;
if (dfs(in, aim, path)){
cout << path;
}
else cout << "失败了";
return 0;
}
姿势点小节:
【1】一维数组去代替二维数组。原因:好遍历、索引/如果二维的规模已知,一维二维之间可相互转化。
【2】康托展开。原因:全排列可以映射到一个数/方便标记以检验是否放入过队列
【3】复制构造函数。在调bug时坑了一把:因为结构体内的函数都是按初始值为0写的,复制构造打破了这个规则。
【4】优先队列比较方法的定义
【5】搜索的实质:利用队/栈去存储节点,并及时标记是否被存储过。
【6】结构体的利用,相当于这个节点的属性。