ZCMU 1639: 残缺的棋盘
Time Limit: 1 Sec Memory Limit: 128 MB
Description
在国际象棋里,王是最重要的一个棋子。每一步,王可以往上下左右或者对角线方向移动一
步,如下图所示。
给定两个格子 A(r1,c1), B(r2,c2),你的任务是计算出一个王从 A 到 B 至少需要走多少步。为了
避免题目太简单,我们从棋盘里拿掉了一个格子 C(r3,c3)(ABC 保证互不相同),要求王从 A
走到 B 的过程中不能进入格子 C。在本题中,各行从上到下编号为 1~8,各列从左到右编号为
1~8。
Input
输入包含不超过 10000 组数据。每组数据包含 6 个整数 r1, c1, r2, c2, r3, c3 (1<=r1, c1, r2, c2, r3,
c3<=8). 三个格子 A, B, C 保证各不相同。
Output
对于每组数据,输出测试点编号和最少步数
Sample Input
1 1 8 7 5 6
1 1 3 3 2 2
Sample Output
Case 1: 7
Case 2: 3
思路
这题其实不仅仅用广搜(BFS),找规律也是可以找出来的,本蒟蒻就在规律的代码实现上卡了老半天......下面是广搜和规律两种不同方法:
广搜
广搜就用广搜的套路,从起点开始,上下左右都加入队列,并对加入队列的点进行标记,防止重复搜索。每次对队列中的点判断其是否处在终点上,若否则将其上下左右的点中没有被加入过队列的,并且没有越过搜索区域边界的点加入队列,若是则可以输出结果。对于每个点我们应当创建一个结构体,存储其位置和当前步数还需要对全图创建一个二维数组,储存各点的入队情况,对于广搜的具体方法本蒟便不在此细说。
广搜AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
int main()
{
int c = 1;
//用于储存队列中点信息
struct node
{
int x;
int y;
int step;
}que[65];
bool book[9][9];
int head, tail;
int c1, r1, c2, r2, c3, r3;
bool flag;
//将下一步能够走的方向存在数组中
int next[8][2] = { { 0,1 },{ 1,0 },{ 0,-1 },{ -1,0 },{ 1,1 },{ 1,-1 },{ -1,-1 },{ -1,1 } };
while (scanf("%d%d%d%d%d%d", &r1, &c1, &r2, &c2, &r3, &c3) != EOF)
{
//初始化队列
head = 1, tail = 1;
flag = false;
memset(book, true, sizeof(book));
//起点入队
que[tail].x = r1;
que[tail].y = c1;
que[tail].step = 0;
book[r1][c1] = false;
tail++;
//标记挖掉的点
book[r3][c3] = false;
while (head < tail)
{
//八个方向都走一次试试
for (int i = 0; i < 8; i++)
{
int tx = que[head].x + next[i][0];
int ty = que[head].y + next[i][1];
//如果越界则不入队
if (tx > 8 || tx < 1 || ty > 8 || ty < 1)continue;
//如果已入队或是走到被挖去的格子则不入队
if (book[tx][ty])
{
que[tail].x = tx;
que[tail].y = ty;
que[tail].step = que[head].step + 1;
book[tx][ty] = false;
tail++;
}
//如果走到终点了则退出循环
if (tx == r2 && ty == c2)
{
flag = true;
break;
}
}
if (flag)break;
head++;
}
printf("Case %d: %d\n", c++, que[tail - 1].step);
}
return 0;
}
规律
规律的代码比广搜什么的短多了!真的!
下面我们对国王行走时可能会遇到的几种情况进行讨论:
当国王在同一行(列)上走时,最短路径仅有一条,步数即为列(行)号差。
当国王需要跨行列行走时,要取得最短步数,我们势必要从对角线方向走,若起终点刚好是正方形的对角线,则最短路径仅有该对角线一条,步数为正方形的边长。
若起终点是长方形的对角线,我们没有办法直接走对角线过去,但是我们可以通过向长边方向走,走到两边长度相同处,再跨过一个正方形得到最短路径,鉴于这个正方形其实走到哪里跨都可以,所以此时有多条最短路径。这些最短路径的步数为此长方形中以短边为边的正方形边长a和该长方形长边长与短边长差值b-a之和。慢着,不就是b吗......
以上三种情况中第一第二种是只有一条最短路径的,因此若是挖掉的格子恰好在起终点之间,为了绕开它,步数必须+1,第三种情况中由于多条路径,所以挖去格子不对其造成影响。
三种情况其实还可以综合一下,不管是走直线,正方形,长方形都可以看做穿过矩形,可以看出每次我们得到的步数均是该矩形的长边。因此我们只需要求出长边,再在前两种情况中将边长+1,即可得出正确结果。
规律AC代码
#include <iostream>
#include <cstdio>
using namespace std;
int main()
{
int c = 1, r1, c1, r2, c2, r3, c3;
while(scanf("%d%d%d%d%d%d",&r1,&c1,&r2,&c2,&r3,&c3)!=EOF)
{
//求得长边
int d = max(abs(r1 - r2), abs(c1 - c2));
//此处将前两种情况下的d值加一
if((c1-c2==r1-r2&&c1-c3==r1-r3||c1-c2==r2-r1&&c1-c3==r3-r1)&&r3>min(r1,r2)&&r3<max(r1,r2))d++;
printf("Case %d: %d\n", c++, d);
}
return 0;
}
广搜你看看人家规律多短