骑士精神
Posted 【對策局】
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了骑士精神相关的知识,希望对你有一定的参考价值。
【题目描述】
在一个5×5的棋盘上有12个白色的骑士和12个黑色的骑士, 且有一个空位。在任何时候一个骑士都能按照骑士的走法(它可以走到和它横坐标相差为1,纵坐标相差为2或者横坐标相差为2,纵坐标相差为1的格子)移动到空位上。
给定一个初始的棋盘,怎样才能经过移动变成如下目标棋盘:
为了体现出骑士精神,他们必须以最少的步数完成任务。
【输入描述】
第一行有一个正整数T(T <= 10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。
【输出描述】
对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。
【样例输入】
2
10110
01*11
10111
01001
00000
01011
110*1
01110
01010
00100
【样例输出】
7
-1
IDA*算法:
源代码: #include<cstdio> #include<iostream> //包括【swap()】函数。 using namespace std; int n,K; int ans[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}}; //目标。 int x[8]={1,1,-1,-1,2,2,-2,-2}; int y[8]={2,-2,2,-2,1,-1,1,-1}; //偷懒。 bool Flag=0; int Judge(int i[5][5]) //检测判断。 { for (int a=0;a<5;a++) for (int b=0;b<5;b++) if (ans[a][b]!=i[a][b]) return 0; return 1; } int Eva(int i[5][5],int s) //相当于一个估价函数,最少(一般不可能实现)的步数都无法满足,自然不成立。 { int v=0; for (int a=0;a<5;a++) for (int b=0;b<5;b++) if (i[a][b]!=ans[a][b]) { v++; if (v+s>K) return 0; } return 1; } void Search(int s,int i[5][5],int X,int Y) { if (s==K) //已到达约定的步数。 { if (Judge(i)) Flag=1; return; } if (Flag) //更快地跳出。 return; for (int a=0;a<8;a++) { int NowX=X+x[a],NowY=Y+y[a]; //移动。 if (NowX<0||NowX>4||NowY<0||NowY>4) //不符实际。 continue; swap(i[X][Y],i[NowX][NowY]); //交换数值。 if (Eva(i,s)) //估计还有没有继续下去的意义。 Search(s+1,i,NowX,NowY); swap(i[X][Y],i[NowX][NowY]); //不行就回溯回来。 } } int main() { scanf("%d",&n); while (n--) { int i[5][5]; //i[]表示当前所示位置的骑士。 int X,Y; //开始时的空格位置。 for (int a=0;a<5;a++) { char T[5]; scanf("%s",T); for (int b=0;b<5;b++) { if(T[b]==\'*\') { i[a][b]=2; X=a; Y=b; } else i[a][b]=T[b]-\'0\'; //0白1黑。 } } for (K=1;K<=15;K++) //迭代约定的步数。 { Search(0,i,X,Y); if (Flag) //完成目标棋局。 { printf("%d\\n",K); break; } } if (!Flag) printf("-1\\n"); else Flag=0; } return 0; }
优化剪枝:
源代码: #include<cstdio> #include<iostream> using namespace std; int Goal[6][6]={{0,0,0,0,0,0}, {0,1,1,1,1,1}, {0,0,1,1,1,1}, {0,0,0,2,1,1}, {0,0,0,0,0,1}, {0,0,0,0,0,0}}; int x[8]={1,1,-1,-1,2,2,-2,-2}; int y[8]={2,-2,2,-2,1,-1,1,-1}; int i[6][6],num[6][6],ans; int Check() //检测。 { int Num=0; for (int a=1;a<=5;a++) for (int b=1;b<=5;b++) if (i[a][b]!=Goal[a][b]) Num++; return Num; } void DFS(int xxx,int yyy,int X,int Y,int s,int limit) //其实当前所用步数为(s-1)步,[xxx,yyy]表示上个节点,[X,Y]表示此节点。 { int t=Check(); if (!t) { ans=min(ans,s-1); return; } if (s+t-2>limit) //最小(一般不可能)估价。 return; if (s>limit) return; for (int a=0;a<8;a++) { int t1=X+x[a]; int t2=Y+y[a]; if (t1>=1&&t1<=5&&t2>=1&&t2<=5&&!(t1==xxx&&t2==yyy)) //避免重复,刚交换的点不能再复回。 { swap(i[X][Y],i[t1][t2]); DFS(X,Y,t1,t2,s+1,limit); swap(i[X][Y],i[t1][t2]); } } } void Init() { int X,Y; ans=16; for (int a=1;a<=5;a++) for (int b=1;b<=5;b++) { char t; cin>>t; if (t==\'*\') { i[a][b]=2; X=a; Y=b; } else i[a][b]=t-\'0\'; } for (int K=1;K<=15;K++) //约定。 { DFS(X,Y,X,Y,1,K); if (ans<16) //OK就打断。 { printf("%d\\n",ans); return; } } printf("-1\\n"); } int main() { int n; scanf("%d",&n); while (n--) Init(); return 0; } /* 思想整理: ①从小到大约定步数; ②估价后再移动搜索; ③搜索中加检验判断; ④有解就跳出并输出。 第二个代码省时的诀窍我觉得是加了节点交换的判重,但依然感觉并不是那么高效。 */
以上是关于骑士精神的主要内容,如果未能解决你的问题,请参考以下文章