骑士精神

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;
}

/*
    思想整理:
        ①从小到大约定步数;
        ②估价后再移动搜索;
        ③搜索中加检验判断;
        ④有解就跳出并输出。
    第二个代码省时的诀窍我觉得是加了节点交换的判重,但依然感觉并不是那么高效。
*/

 

以上是关于骑士精神的主要内容,如果未能解决你的问题,请参考以下文章

骑士精神——IDA*

骑士精神

BZOJ1085: [SCOI2005]骑士精神

BZOJ1085: [SCOI2005]骑士精神

BZOJ 1085 [SCOI2005]骑士精神

bzoj1085: [SCOI2005]骑士精神(a*)