zoukankan      html  css  js  c++  java
  • hiho #1308 : 搜索二·骑士问题

    #1308 : 搜索二·骑士问题

    时间限制:10000ms
    单点时限:1000ms
    内存限制:256MB

    描述

    小Hi:小Ho你会下国际象棋么?

    小Ho:应该算会吧,我知道每个棋子的移动方式,马走日象飞田什么的...

    小Hi:象飞田那是中国象棋啦!

    小Ho:哦,对。国际象棋好像是走斜线来着。

    小Hi:不过马走日倒是对了。国际象棋中的马一般叫做骑士,关于它有个很有意思的问题。

    小Ho:什么啊?

    小Hi:骑士巡游问题,简单来说就是关于在棋盘上放置若干个骑士,然后探究移动这些骑士是否能满足一定的而要求。举个例子啊:一个骑士从起始点开始,能否经过棋盘上所有的格子再回到起点。

    小Ho:哦,看上去好像很难的样子。

    小Hi:其实也还好了。简单一点的比如棋盘上有3个骑士,能否通过若干次移动走到一起。

    小Ho:能够么?

    小Hi:当然能够了。由于骑士特殊的移动方式,放置在任何一个初始位置的骑士,都可以通过若干次移动到达棋盘上任意一个位置。

    小Ho:那么只要选定一个位置,把它们全部移动过去就好了是吧?

    小Hi:是的,那么这里又有另一个问题了:要选择哪一个位置汇合,使得3个骑士行动的总次数最少?

    小Ho:嗯,这个好像不是很难,让我想一想。

    提示:骑士问题

    输入

    第1行:1个正整数t,表示数据组数,2≤t≤10。

    第2..t+1行:用空格隔开的3个坐标, 每个坐标由2个字符AB组成,A为'A'~'H'的大写字母,B为'1'~'8'的数字,表示3个棋子的初始位置。

    输出

    第1..t行:每行1个数字,第i行表示第i组数据中3个棋子移动到同一格的最小行动步数。

    样例输入
    2
    A1 A1 A1
    B2 D3 F4
    样例输出
    0
    2

    思路:

    利用队列做bfs,求出当前位置到棋盘上所有位置的最短距离,然后,三个棋子到棋盘位置的最短距离和

    AC代码:

     1 #include "iostream"
     2 #include "string.h"
     3 #include "queue"
     4 
     5 using namespace std;
     6 
     7 typedef pair<int, int> pii;
     8 char ss[3];
     9 int vis[3][8][8];
    10 int d[8][2] = { { -2,1 },{ -2,-1 },{ -1,-2 },{ -1,2 },{ 2,-1 },{ 2,1 },{ 1,-2 },{ 1,2 } };
    11 int step = 0;
    12 
    13 pii pos; //马位置
    14 
    15 bool in(pii p)
    16 {
    17     if (p.first < 0 || p.second < 0 || p.first>7 || p.second>7)
    18         return false;
    19     else
    20         return true;
    21 }
    22 
    23 void bfs(int vi[8][8])
    24 {
    25     step = 0;
    26     queue<pii> q;
    27     memset(vi, -1, 256);
    28     q.push(pos);
    29     vi[pos.first][pos.second] = 0;
    30 
    31     while (!q.empty())
    32     {
    33         pii pfront = q.front();
    34         q.pop();
    35         for (int i = 0; i < 8; i++)
    36         {
    37             pii temp;
    38             temp.first = pfront.first + d[i][0];
    39             temp.second = pfront.second + d[i][1];
    40             if (in(temp) && vi[temp.first][temp.second] == -1)
    41             {
    42                 vi[temp.first][temp.second] = vi[pfront.first][pfront.second] + 1;
    43                 q.push(temp);
    44             }
    45         }
    46     }
    47 }
    48 
    49 int main()
    50 {
    51     int t;
    52     cin >> t;
    53     while (t--)
    54     {
    55         scanf("%s", ss);
    56         pos = make_pair(ss[0] - 'A', ss[1] - '1');
    57         bfs(vis[0]);
    58 
    59         scanf("%s", ss);
    60         pos = make_pair(ss[0] - 'A', ss[1] - '1');
    61         bfs(vis[1]);
    62 
    63         scanf("%s", ss);
    64         pos = make_pair(ss[0] - 'A', ss[1] - '1');
    65         bfs(vis[2]);
    66 
    67         int ans = 1e9;
    68         for (int i = 0; i < 8; i++)
    69         {
    70             for (int j = 0; j < 8; j++)
    71             {
    72                 int temp = 0;
    73                 for (int k = 0; k < 3; k++)
    74                 {
    75                     temp += vis[k][i][j];
    76                 }
    77                 if (temp < ans)
    78                     ans = temp;                
    79             }            
    80         }
    81         cout << ans << endl;        
    82     }
    83 }

    补充:

    提供另一种思路,因为是8x8的棋盘,可以模拟成8进制的一个数,这样3个旗子就是一个6位的8进制数。

    由此可以通过一个大小为8^6的布尔数组来进行状态的判重。而每一次的状态转移也从原来的仅枚举8个方向,变成了枚举骑士加枚举方向,一共有3*8=24种可能。

    此方法的伪代码为:

    queue.push( initialStatus ) // 将初始的8进制数加入队列中
    while (!queue.isEmpty())
    	now_status = queue.pop()	// 弹出队列头元素
    	For i = 1 .. 3
    	// 枚举移动的其实
    		For j = 1 .. 8
    		// 枚举8种可能的移动
    		next_status = move(now_status, i, j)	// 移动骑士并记录状态	
    		If (next_status is valid AND not visited[ next_status ])
    			step[ next_status ] = step[ now_status ] + 1
    			queue.push( next_status )
    			If (check(next_status)) Then
    				// 检查这个八进制数是否满足3个坐标重合
    				Return step[ next_status ]
    			End If 
    		End If
    	End For
    End While

    在进行检查是否已经走到一起时,可以通过一个位运算来做:

    check(status):
    	Return ((status and 0x3f) == ((status rsh 6) and 0x3f)) and (((status rsh 6) and 0x3f) == ((status rsh 12) and 0x3f))
    	// rsh表示右移操作

    小Ho:哦,这样就可以不用计算出每个骑士走到每个点的步数,而是在过程中就有可能直接求解到最先汇合位置的步数。

    小Hi:对,不过这个算法中状态的转移会稍微复杂一点。你可以选择一个你比较喜欢的方法来实现。

    小Ho:好!

  • 相关阅读:
    随机生成手机号,QQ号,姓名...小工具类汇总
    简单的时间格式转换工具类(java)
    判断某个时间点在一个时间段内方法汇总
    AES加密解密代码(key是16位)--java
    Linux下查看CPU、内存、磁盘信息
    idea如何优雅部署项目到weblogic
    weblogic部署项目
    程序员必知的十大基础实用算法及其讲解
    Java开发工具包 ==> Hutool
    java基础-泛型举例详解
  • 原文地址:https://www.cnblogs.com/SeekHit/p/6573314.html
Copyright © 2011-2022 走看看