yzoi2226最小步数的详细解法

Description - 问题描述

  在各种棋中,棋子的走法总是一定的,如中国象棋中马走“日”。有一位小学生就想如果马能有两种走法将增加其趣味性,因此,他规定马既能按“日”走,也能如象一样走“田”字。他的同桌平时喜欢下围棋,知道这件事后觉得很有趣,就想试一试,在一个(100*100)的围棋盘上任选两点A、B,A点放上黑子,B点放上白子,代表两匹马。棋子可以按“日”字走,也可以按“田”字走,俩人一个走黑马,一个走白马。谁用最少的步数走到左上角坐标为(1,1)的点时,谁获胜。现在他请你帮忙,给你A、B两点的坐标,想知道两个位置到(1,1)点可能的最少步数。

Input - 输入数据

  两行:第一行A点的坐标,第二行B点的坐标,坐标都是不超过100的自然数。

Output - 输出数据

  两行,第一行A点走到(1,1)位置的最少步数;第二行是B点走到(1,1)位置的最少步数。

详细解法

  读完题目我们可以知道,这里的马可以走“日”字,亦可以走“田”字,那么我们假设该点的坐标为(x,y),那么其可以到达的坐标便有12种情况,分别为:走“日”字:(x-2,y-1),(x-1,y-2),(x-2,y+1),(x-1,y+2),(x+2,y-1),(x+1,y-2),(x+1,y+2),(x+2,y+1);走“田”字:(x-2,y-2),(x-2,y+2),(x+2,y+2),(x+2,y-2)。这样分析下来,我们便可将所有x的增量以及其所对应的y的增量保存到两个数组dx[],dy[]中,每次通过对数组对应元素的选择来完成移动。代码如下:

int dx[12]={-2,-2,-1,1,2,2,2,2,1,-1,-2,-2};
int dy[12]={-1,-2,-2,-2,-2,-1,1,2,2,2,2,1};

  其后,题目要求是分别有两颗棋子进行移动,其最终目的地都是(1,1),那么,我们需要对每个棋子的移动方式进行探讨吗?其实不然,对于每一颗棋子,虽然他们的出发点不同,但其终点都为(1,1)。以是,我们便可看成是有一颗棋子从(1,1)出发,分别移动到(x1,y1),(x2,y2)的步数。这样一来,便可以通过bfs(百度定义:宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。)来完成整个的搜索最少步数的工作了。

  既然确定了bfs的算法,那么我们就需要使用队列的结构了,我们可以用STL里的queue来定义一个队列数组,代码如下:

#include<queue>
queue<int>q[4];

  其中,q[1]存储从(1,1)可到达点的横坐标,q[2]存储从(1,1)可到达点的纵坐标,q[3]则表示到达该点所需要的最短步数。假如你说用不惯STL里的queue,那么我们也可以自己写队列,如下所示:

int que[maxn][4]={0};

  同时也需要定义一个头指针与一个尾指针,其二者的初值都为1,以表示队列为空,代码如下:

int head=1,tail=1;

  其次,为了最后输出的方便,我们还要定义一个s[maxn][maxn]数组来存储到达s[x][y]的最小步数,最后只需要输出s[x1][y1],s[x2][y2]的值即可。同时,也得将s[1][1]赋值为0,其他元素赋值为1,代码如下:

memset(s,-1,sizeof(s));
s[1][1]=0;

(接下来,我们全部用自己写的队列,进行操作,使用STL代码将在最后给出)

  一开始,我们将初始状态入队,即q[1][1]=1,q[1][2]=1,q[1][3]=0,代码如下:

    q[1][1]=1;
    q[1][2]=1;
    q[1][3]=0;    

  接着,我们便使用一个循环进行拓展的操作,和1777的倒水一样,循环成立的条件是head<=tail(即队列不为空).然后,对于前面所列出的12种情况,我们每一种都要进行探讨,即使用一个for循环,从0枚举到11.其步骤大致如下:①取队首元素所能到达的位置②判断当前位置是否越界③判断当前位置是否到达过(根据bfs的原理先前到达过的步数一定小于当前的步数,故不需要再继续拓展)④将当前的s[x][y]的值更新,即其值就等于当前q[head][3]+1⑤将新得到的x,y的值入队⑥判断是否已将(x1,y1),(x2,y2)的最少步数都搜索到,即判断s[x1][y1],s[x2][y2]的值是否都>0,是的话就输出后结束程序否则就弹出队首元素(前面只是取出,并没有弹出)并重复①。其代码如下:

 1     while(head<=tail)
 2     {
 3         for(int d=0;d<12;d++)
 4         {
 5             int x=q[head][1]+dx[d];    //取队首元素所能到达横坐标的位置
 6             int y=q[head][2]+dy[d];    //取队首元素所能到达纵坐标的位置
 7             if(x>0&&y>0&&x<=100&&y<=100)    //判断是否在界内
 8                 if(s[x][y]==-1)        //判断是否已经拓展过
 9                 {
10                     s[x][y]=q[head][3]+1;    //更新s[x][y]的值
11                     tail++;                //将新状态入队
12                     q[tail][1]=x;
13                     q[tail][2]=y;
14                     q[tail][3]=s[x][y];
15                     if(s[x1][y1]>0&&s[x2][y2]>0)    //判断是否达到了目标状态
16                     {
17                         cout<<s[x1][y1]<<endl;    //输出
18                         cout<<s[x2][y2]<<endl;
19                         return 0;
20                     }
21                 }
22         }
23         head++;        //弹出队首元素
24     }

  最后,给出OJ上ac的代码,仅供参考:

  以下下为自己写的队列:

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int const maxn=10000+1;
 5 int const area=100+1;
 6 int dx[12]={-2,-2,-1,1,2,2,2,2,1,-1,-2,-2};
 7 int dy[12]={-1,-2,-2,-2,-2,-1,1,2,2,2,2,1};
 8 int s[area][area],q[maxn][4]={0};
 9 int x1,y1,x2,y2;
10 int head=1,tail=1;
11 int main()
12 {
13     memset(s,0xff,sizeof(s));
14     cin>>x1>>y1>>x2>>y2;
15     q[1][1]=1;
16     q[1][2]=1;
17     q[1][3]=0;
18     while(head<=tail)
19     {
20         for(int d=0;d<12;d++)
21         {
22             int x=q[head][1]+dx[d];    //取队首元素所能到达横坐标的位置
23             int y=q[head][2]+dy[d];    //取队首元素所能到达纵坐标的位置
24             if(x>0&&y>0&&x<=100&&y<=100)    //判断是否在界内
25                 if(s[x][y]==-1)        //判断是否已经拓展过
26                 {
27                     s[x][y]=q[head][3]+1;    //更新s[x][y]的值
28                     tail++;                //将新状态入队
29                     q[tail][1]=x;
30                     q[tail][2]=y;
31                     q[tail][3]=s[x][y];
32                     if(s[x1][y1]>0&&s[x2][y2]>0)    //判断是否达到了目标状态
33                     {
34                         cout<<s[x1][y1]<<endl;    //输出
35                         cout<<s[x2][y2]<<endl;
36                         return 0;
37                     }
38                 }
39         }
40         head++;        //弹出队首元素
41     }
42 }

  以下为直接用STL的队列,其大致入队出队的操作都与前者相似,故不再介绍:

 1 /*
 2     Name: lwq
 3     Copyright:
 4     Author:
 5     Date: 14-12-14 14:17
 6     Description: 2226STL
 7 */
 8
 9 #include<iostream>
10 #include<cstring>
11 #include<queue>
12 using namespace std;
13 int const maxn=10000+1;
14 int const area=100+1;
15 int dx[12]={-2,-2,-1,1,2,2,2,2,1,-1,-2,-2};
16 int dy[12]={-1,-2,-2,-2,-2,-1,1,2,2,2,2,1};
17 int s[area][area];
18 queue<int>q[4];
19 int x1,y1,x2,y2;
20 int main()
21 {
22     memset(s,-1,sizeof(s));
23     cin>>x1>>y1>>x2>>y2;
24     q[1].push(1);
25     q[2].push(1);
26     q[3].push(0);
27     while((!q[1].empty())&&(!q[2].empty())&&(!q[3].empty()))
28     {
29         for(int d=0;d<12;d++)
30         {
31             int x=q[1].front()+dx[d];
32             int y=q[2].front()+dy[d];
33             if(x>0&&y>0&&x<=100&&y<=100)
34                 if(s[x][y]==-1)
35                 {
36                     s[x][y]=q[3].front()+1;
37                     q[1].push(x);
38                     q[2].push(y);
39                     q[3].push(s[x][y]);
40                     if(s[x1][y1]>0&&s[x2][y2]>0)
41                     {
42                         cout<<s[x1][y1]<<endl;
43                         cout<<s[x2][y2]<<endl;
44                         return 0;
45                     }
46                 }
47         }
48         q[1].pop();
49         q[2].pop();
50         q[3].pop();
51     }
52 }

  最后,欢迎大家的指教。

时间: 2024-07-30 00:06:54

yzoi2226最小步数的详细解法的相关文章

(hdu step 4.2.4)A strange lift(求从起点到终点的最小步数,限制条件是:在一维的情况下)

题目: A strange lift Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 709 Accepted Submission(s): 348   Problem Description There is a strange lift.The lift can stop can at every floor as you want, a

One Person Game(扩展欧几里德求最小步数)

One Person Game Time Limit: 2 Seconds      Memory Limit: 65536 KB There is an interesting and simple one person game. Suppose there is a number axis under your feet. You are at point A at first and your aim is point B. There are 6 kinds of operations

(hdu step 4.2.3)Knight Moves(求从起点是否能够到达终点的最小步数)

题目: Knight Moves Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 453 Accepted Submission(s): 326   Problem Description A friend of you is doing research on the Traveling Knight Problem (TKP) where

搜索学习(2)--NYOJ 58 最小步数

题目链接:click here 搜索入门题, 题意:给定一张8*8的图,给定两个坐标:起点和终点,规定0为可以走,1为墙,不能走,求出起点走到终点的最小步数. dfs的基础应用 参考代码: #include <cmath> #include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using name

POJ 1753 Flip Game (高斯消元 枚举自由变元求最小步数)

题目链接 题意:4*4的黑白棋,求把棋全变白或者全变黑的最小步数. 分析:以前用状态压缩做过. 和上题差不多,唯一的不同是这个终态是黑棋或者白棋, 但是只需要把给的初态做不同的两次处理就行了. 感觉现在还只是会套模板,不能独立的思考,好伤心.... 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath&g

2013 到1 通过最小步数 到达1 2中运算 -1 或者除以2

d[n]=min(dp[n-1]+1,dp[n.2]): n为偶数 dp[n]=dp[n-1]+1; 答案为18 怎么通过计算求得答案? #include<iostream> using namespace std; int d[2014]; int min(int x,int y) { if(x>y) return y; else return x; } int main() { int n; cin>>n; memset(d,0,sizeof(d)); d[1]=0; d

Algorithm --&gt; 求出A到B的最小步数

求出A到B的最小步数 给定象棋盘,以及位置A和B, 求出从A到B的最小步数 Input 2             -->case 个数9 9          -->棋盘大小3 5 2 8    --> 起始位置20 202 3 7 9 Output2 5 代码: #include <cstdio> #include <iostream> #include <string.h> using namespace std; #define MAX 100

Knight&#39;s Trip 马在无线大棋盘上跳到指定点最小步数问题

题目描述 Problem D: Knight's Trip In chess, each move of a knight consists of moving by two squares horizontally and one square vertically, or by one square horizontally and two squares vertically. A knight making one move from location (0,0) of an infin

ybt 1252 广度优先搜索 走迷宫(二维、最小步数)

1252:走迷宫 时间限制: 1000 ms         内存限制: 65536 KB提交数: 7272     通过数: 3241 [题目描述] 一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走:有的格子是空地,可以走. 给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到).只能在水平方向或垂直方向走,不能斜着走. [输入] 第一行是两个整数,R和C,代表迷宫的长和宽.( 1≤ R,C ≤ 40) 接下来是R行,每行C个字符,代表整个迷宫. 空地格子用‘.’表示,