一道经典的IDA*入门题,本质上就是dfs+估价函数
思路:以唯一的空位为起点,进行dfs,利用估价函数进行剪枝。
for(int dep=1;maxdep<=15;dep++){ //dep:本次需要移动的次数,因为从1开始,一但满足条件,就是题目所要求的最小步数
A_star(0,stx,sty,dep); //dfs代码,stx sty:空位所在位子
if(success){ //如果满足条件,输出步数
printf("%d\n",dep);
}
}
评估函数:
评估函数是理想的情况,而实际情况一定会大于等于评估函数,但如果评估函数越接近实际情况,那么剪枝效果就越好。
int evaluate(){
int cnt=0;
for(int i=0;i<5;i++)
for(int j=0;j<5;j++)
if(mp[i][j]!=goal[i][j])cnt++; //如果现棋盘与目标棋盘不符,则cnt(至少需要的步数)加1
return cnt;
}
dfs:
void a_star(int dep,int x,int y,int maxdep) {
if (dep==maxdep){ //返回条件是当前搜索数达到了最大搜索数(也就是一开始传进来的dep)
if(evaluate() == 0) //如果评估函数为0,说明现棋盘与目标棋盘一致
success = 1;
return;
}
int mx[8] = { -2,-2,-1,1,-1,1,2,2 }; //棋子走的八个方向
int my[8] = { -1,1,2, 2,-2,-2,-1,1 };
for (int i = 0; i < 8; i++) {
int xx = x + mx[i];
int yy = y + my[i];
if (!check(xx, yy)) //check()判断移动后的棋子是否越界
continue;
swap(a[x][y], a[xx][yy]); //交换两棋子
if (evaluate() + dep <= maxdep) //如果评估函数的步数加当前步数未达到限制步数,进行迭代加深,这一步达到剪枝效果
a_star(dep + 1, xx, yy, maxdep); //迭代加深
swap( a[x][y],a[xx][yy]); //记得要回溯回来
}
}
总代码:
#include<iostream>
using namespace std;
int goal[5][5] = {
{1,1,1,1,1},
{0,1,1,1,1},
{0,0,2,1,1},
{0,0,0,0,1},
{0,0,0,0,0} };//1黑,0白
int x, y,m,n,success,a[5][5],flag;
int mx[8] = { -2,-2,-1,1,-1,1,2,2 };
int my[8] = { -1,1,2, 2,-2,-2,-1,1 };
int evaluate() { //价值评估
int cnt = 0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (a[i][j] != goal[i][j])
cnt++;
}
}
return cnt;
}
bool check(int x, int y) { //判断棋子有无越界
if(x<0||x>4||y<0||y>4)
return false;
return true;
}
void a_star(int dep,int x,int y,int maxdep) {
if (dep==maxdep){
if(evaluate() == 0)
success = 1;
return;
}
for (int i = 0; i < 8; i++) {
int xx = x + mx[i];
int yy = y + my[i];
if (!check(xx, yy))
continue;
swap(a[x][y], a[xx][yy]);
if (evaluate() + dep <= maxdep)
a_star(dep + 1, xx, yy, maxdep);
swap( a[x][y],a[xx][yy]);
}
}
int main() {
char c;
int t;
cin >> t;
while (t) {
success = 0;
flag = 0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
cin >> c;
if (c != '*')
a[i][j] = c - '0';
else {
a[i][j] = 2;
n = i;
m = j;
}
}
}
if (evaluate() == 0) {
cout << 0 << endl;
continue;
}
for (int i = 1; i <= 10; i++) {
a_star(0, n, m, i);
if (success == 1) {
cout << i << endl;
flag = 1;
break;
}
}
if (flag == 0)
cout << -1 << endl;
t--;
}
return 0;
}
ps:今天看这题,才认识了A*,尽管这是一道入门题,但自己还是花了一晚上搞定,要多练习。