CSG 1195:华容道

原题链接:https://cpc.csgrandeur.cn/csgoj/problemset/problem?pid=1195

题目内容

Description
在一个n × m的棋盘上有n行m列共n × m个格子,其中有且只有一个格子是空白的,其余n × m − 1个格子上各有一个棋子. 每一步可以将空白的格子上、下、左、右相邻的格子上的棋子移动到空白格子上.

我们需要计算将某个位置的棋子移动到指定位置的最小步数.

Input
包含不超过1000组测试数据.

每组测试数据包括8个整数n, m, x, y, r1, c1, r2, c2,表示棋盘的大小为n × m,第x行y列的格子是空白的,需要将第r1行c1列的棋子移动到第r2行c2列.

2 ≤ n ≤ 108,2 ≤ m ≤ 108.

Output
输出将棋子移动到指定位置的最小步数.

Sample Input
2 2 1 2 1 1 1 2
2 3 2 2 1 1 2 3
Sample Output
1
8

解题思路

下面会将空白的部分称为空白格,最初的格子称为初始块

  • 首先明确一件事:假设空白格在初始块旁边,当空白格在当前移动方向的反方向时,移动所需的步数为5,当所在位置与移动方向垂直时,步数为3,若正好在要移动的方向,则步数为1,如图:
    在这里插入图片描述

  • 其次,阶梯型的移动方式所需步数一定比直线型移动要少。因为每次移动,初始块都会与空白格交换位置,若进行直线型移动,那么每次移动后到下一格都需要 5 步(原因如上),而阶梯型移动能保证移动方向与块的位置垂直,故只需要 3 步。

  • 由以上两点可得,目标块最佳的移动方案应该是先阶梯型移动,再直线移动到达终点。

  • 空白格的移动路径只需直接计算横纵距离绝对值之和即可,若出现需要穿过目标块的移动,则需要在步数上 +2。

  • 至于阶梯型移动应该先横向走还是先纵向走,可以两种都模拟一遍,不用细想。

Ac代码

#include <bits/stdc++.h>

using namespace std;

int main() {
    int n, m, x, y, r1, c1, r2, c2;
    int d_x, d_y, dir_x, dir_y, tx, ty;
    int step, tmp_step;
    while (cin >> n >> m >> x >> y >> r1 >> c1 >> r2 >> c2) {
        dir_x = dir_y = 0;
        d_x = r2 - r1, d_y = c2 - c1;
        if (d_x < 0)
            d_x = -d_x, dir_x = -1;
        else if (d_x > 0)
            dir_x = 1;
        if (d_y < 0)
            d_y = -d_y, dir_y = -1;
        else if (d_y > 0)
            dir_y = 1;

        if (d_x == 0) {  // 同行
            tx = r1, ty = c1 + dir_y;
            step = abs(x - tx) + abs(y - ty) + d_y * 5 - 4;
            if (x == r1 && (y - c1) * dir_y < 0) step += 2;
        } else if (d_y == 0) {  // 同列
            tx = r1 + dir_x, ty = c1;
            step = abs(x - tx) + abs(y - ty) + d_x * 5 - 4;
            if (y == c1 && (x - r1) * dir_x < 0) step += 2;
        } else {
            // 先纵向走
            tx = r1 + dir_x, ty = c1;
            step = abs(x - tx) + abs(y - ty) + min(d_x, d_y) * 3 * 2 - 2 + abs(d_x - d_y) * 5;
            if (y == c1 && (x - r1) * dir_x < 0) step += 2;//空白格需要穿过目标块
            if (d_x > d_y) step -= 2;//这种情况下阶梯型移动其实还剩最后一步
            // 先横向走
            tx = r1, ty = c1 + dir_y;
            tmp_step = abs(x - tx) + abs(y - ty) + min(d_x, d_y) * 6 - 2 + abs(d_x - d_y) * 5;
            if (x == r1 && (y - c1) * dir_y < 0) tmp_step += 2;//空白格需要穿过目标块
            if (d_x < d_y) tmp_step -= 2;
            step = min(step, tmp_step);
        }
        cout << step << endl;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值