zoukankan      html  css  js  c++  java
  • 骑士精神

    【题目描述】

    在一个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
    ",K);
                    break;
                }
            }
            if (!Flag)
              printf("-1
    ");
            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
    ",ans);
                return;
            }
        }
        printf("-1
    ");
    }
    int main()
    {
        int n;
        scanf("%d",&n);
        while (n--)
          Init();
        return 0;
    }
    
    /*
        思想整理:
            ①从小到大约定步数;
            ②估价后再移动搜索;
            ③搜索中加检验判断;
            ④有解就跳出并输出。
        第二个代码省时的诀窍我觉得是加了节点交换的判重,但依然感觉并不是那么高效。
    */
  • 相关阅读:
    转--- 一些概念不错的理解
    python 生产者 --- 消费者
    python GUI 之 tkinter
    读DataSnap源代码(二)
    读DataSnap源代码(一)
    FireDAC探索 (二)
    FireDAC内部初探
    C++Builder XE7 中“匿名”方法实现
    DelphiXE7 Datasnap TDSClientCallbackChannelManager内部实现初探
    C++ Builder使用VC DLL
  • 原文地址:https://www.cnblogs.com/Ackermann/p/5573293.html
Copyright © 2011-2022 走看看