今天下午学习了一下IDA*算法,收获还是不小的。
关于IDA*算法的和此题的题解见下面博客:
http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5856756
讲得非常详细,我的代码也算参考上面那篇博客的。
ID即迭代加深(Iterative Deepening),不断加深允许的最大DFS深度,避免了DFS一条到走到死的情况。虽然出现了某些状态重复搜索的弊端,但是从空间上讲,没有像BFS那样耗费大量内存。
A*算法使用估价函数进行剪枝。
同A*一样,难点是找到合适的估价函数。此题的估价函数剪枝为:如果8-中间最多的数的个数>限定的迭代深度-当前DFS深度则退出搜索。
/*************************************************************************
> File Name: 4106.cpp
> Author:wangxin
> Mail:
> Created Time: 2015年07月10日 星期五 16时25分53秒
************************************************************************/
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int DEPTH,ansnum; //迭代深度,中间的数
int map[25]; //井字格
int midblocks[8]={7,8,9,12,13,16,17,18}; //位于井字中间位置的编号
char ansPath[100]; //存储搜索路径
//判断是否完成搜索
bool Judge(const int* state)
{
int i;
for(i=1;i<=7;i++)
if(state[midblocks[i]]!=state[midblocks[i-1]])
return false;
return true;
}
//进行状态转换
void ChangeState(int *state,int a1,int a2,int a3,int a4,int a5,int a6,int a7)
{
int temp;
temp=state[a1]; state[a1]=state[a2]; state[a2]=state[a3];
state[a3]=state[a4]; state[a4]=state[a5]; state[a5]=state[a6];
state[a6]=state[a7]; state[a7]=temp;
}
//计算中间出现最多的数字的个数
int CountMaxSameNum(const int* state)
{
int i,j,cntarr[4];
memset(cntarr,0,sizeof(cntarr));
for(i=0;i<8;i++) cntarr[state[midblocks[i]]]++;
j=0;
for(i=1;i<=3;i++)
if(cntarr[i]>cntarr[j]) j=i;
return cntarr[j];
}
//DFS函数,返回值表示是否搜索到 curDepth表示当前的搜索深度,preDir表示上一步的变换方向
bool DFS(int *state, int curDepth, int preDir)
{
int i,j;
int temp[25];
//IDA*估价函数剪枝,如果剩下的搜索深度不可能完成任务,不进行进一步搜索
if(DEPTH-curDepth < 8-CountMaxSameNum(state))
return false;
//限定迭代深度
if(curDepth>DEPTH)
return false;
//枚举变换的方向
for(i=1;i<=8;i++)
{
//剪枝:连续的方向相反的变换是没有意义的
if((i==1 && preDir==6)||(i==6 && preDir==1)) continue;
if((i==2 && preDir==5)||(i==5 && preDir==2)) continue;
if((i==3 && preDir==8)||(i==8 && preDir==3)) continue;
if((i==4 && preDir==7)||(i==7 && preDir==4)) continue;
for(j=1;j<=24;j++) temp[j]=state[j];
switch(i)
{
case 1:ansPath[curDepth]='A'; ChangeState(temp, 1, 3, 7,12,16,21,23);break;
case 2:ansPath[curDepth]='B'; ChangeState(temp, 2, 4, 9,13,18,22,24);break;
case 3:ansPath[curDepth]='C'; ChangeState(temp,11,10, 9, 8, 7, 6, 5);break;
case 4:ansPath[curDepth]='D'; ChangeState(temp,20,19,18,17,16,15,14);break;
case 5:ansPath[curDepth]='E'; ChangeState(temp,24,22,18,13, 9, 4, 2);break;
case 6:ansPath[curDepth]='F'; ChangeState(temp,23,21,16,12, 7, 3, 1);break;
case 7:ansPath[curDepth]='G'; ChangeState(temp,14,15,16,17,18,19,20);break;
case 8:ansPath[curDepth]='H'; ChangeState(temp, 5, 6, 7, 8, 9,10,11);break;
default: printf("There is something wrong!");
}
//如果检测到搜索成功,返回true
if (Judge(temp))
{
ansnum=temp[7];
ansPath[curDepth+1]='\0';
return true;
}
if (DFS(temp,curDepth+1,i)) return true;
}
return false;
}
void work()
{
int i,j,k,t,m,n;
for(i=2;i<=24;i++) scanf("%d",&map[i]);
if(Judge(map))
{
printf("No moves needed\n");
printf("%d\n",map[7]);
}
else
{
//限定初始的迭代深度
DEPTH=1;
while(1)
{
if(DFS(map,0,-1)) break;
DEPTH++;
//如果在当前迭代深度下没有搜出答案,加大搜索深度
}
printf("%s\n",ansPath);
printf("%d\n",ansnum);
}
}
int main()
{
while(scanf("%d",&map[1]),map[1])
{
work();
}
return 0;
}