UVa - 1589 : 逻辑判断

题目描述

判断任意红方车、马、炮、帅是否将死黑方单个将。

棋盘为横纵 10 x 9 的交叉点,左上坐标为 (1,1) ,右下坐标为 (10,9) 。黑将可以走到的范围为 ([1, 3], [4, 6]) 。棋子走法等规则详见题述。

输入

输入含多个实例,总数不超过40组。每组实例包含红方将军时的局面。实例第一行分别为三个数字,表示红方棋子数 N (2 <= N <= 7) 、黑将的横坐标 X 、纵坐标 Y ;后接 N 行,每行包括字符 C (棋子类别, C(炮)、R(车)、H(马)、G(将)), 数字 x 和 y (棋子横、纵坐标) 。实例之间由空行隔开。N = X = Y = 0 表示输入结束,此组数据不处理。

输出

每组输出占一行,若能将死输出 "YES",否则输出 "NO" 。

样例

input

2 1 4
G 10 5
R 6 4
3 1 5
H 4 5
G 10 5
C 7 5
0 0 0

output

YES
NO

分析

这道题数据量非常小,但逻辑复杂。可以用面向对象的思路简化代码并增强可读性。

首先抽象出棋子类。由于没有持续走子的要求,只需要实现两种用于判断的方法:一是判断该棋子是否能走到位置A,二是判断棋子是否阻碍了一次移动。位置坐标可以用一个棋子表示,相对的,一次移动用两个棋子表示。

然后是棋盘类。棋盘类是输入数据的简单存储,同时实现判断当前局面是否将死的方法。

代码实现

首先写好棋子类。这是主要的细节处理部分。

struct Piece {
    char name; int x, y;
    Piece(int a, int b): name{'G'}, x{a}, y{b} {} // 构筑用于表示坐标的棋子
    Piece(): name{}, x{}, y{} {
        // 构筑实际棋子,从输入流读入
        cin >> name >> x >> y;
    }
    bool capture(Piece &position) {
        // 是否能走到某位置(吃子)
        switch (name) {
            case 'G': return y == position.y; break;
            case 'C': // 炮无法进行简单判断,需要计数阻碍棋子的个数
                      // 炮同车
            case 'R': return x == position.x || y == position.y; break;
            case 'H': if (position.x == x + 1 || position.x == x - 1) {
                return position.y == y + 2 || position.y == y - 2;
            } else if (position.y == y + 1 || position.y == y - 1) {
                return position.x == x + 2 || position.x == x - 2;
            } break;
            default: cerr << "unreachable branch\n"; break;
        }
        return false;
    }
    bool block(Piece &from, Piece &to) {
        // 是否阻碍了一次移动(from -> to)
        switch (from.name) {
            case 'G': if (from.y == to.y && from.y == y) {
                return (x > from.x && x < to.x) || (x < from.x && x > to.x);
            } break;
            case 'C': // 炮无法进行简单判断,需要计数阻碍棋子的个数
                      // 炮同车
            case 'R': if (from.x == to.x && from.x == x) {
                return (y > from.y && y < to.y) || (y < from.y && y > to.y);
            } else if (from.y == to.y && from.y == y) {
                return (x > from.x && x < to.x) || (x < from.x && x > to.x);
            } break;
            case 'H':
                if (from.y + 2 == to.y && from.y + 1 == y && from.x == x) return true;
                if (from.y - 2 == to.y && from.y - 1 == y && from.x == x) return true;
                if (from.x + 2 == to.x && from.x + 1 == x && from.y == y) return true;
                if (from.x - 2 == to.x && from.x - 1 == x && from.y == y) return true;
                break;
            default: cerr << "unreachable branch\n"; break;
        }
        return false;
    }
};

然后是棋盘类。

struct Board {
    int sz, x, y; vector<Piece> board;
    Board() = default;
    bool get() {
        // 清空棋局,从输入读入一组数据
        board.clear(); sz = x = y = 0;
        cin >> sz >> x >> y;
        for (int i = 0; i < sz; ++i) {
            Piece p; board.push_back(p); // 读入一个棋子的数据
        }
        return sz != 0 || x != 0 || y != 0; // 只有全为0时终止循环
    }
    Piece position{x,y}; Piece &pos(int a, int b) {
        // 构筑一个位置
        position.x = a; position.y = b;
        return position;
    }
    bool checkmate() {
        // 是否将死
        return bad_move(pos(x-1, y)) && bad_move(pos(x+1, y))
            && bad_move(pos(x, y-1)) && bad_move(pos(x, y+1));
    }
    int blocked(Piece &from, Piece &to) {
        // 阻碍移动(from -> to)的棋子个数
        int cnt{0}; for (Piece &p : board) {
            if (p.block(from, to)) ++cnt;
        }
        return cnt;
    }
    bool bad_move(Piece &m) {
        // 该位置是否不安全
        if (m.x < 1 || m.x > 3 || m.y < 4 || m.y > 6) return true; // 超出范围
        for (Piece &p : board) if (m.x != p.x || m.y != p.y) {
            // 遍历有效棋子
            if (p.capture(m)) {
                if (blocked(p, m) == (p.name == 'C' ? 1 : 0)) return true;
            }
        }
        return false;
    }
};

主函数很简单。

int main() {
    Board board;
    while (board.get()) {
        cout << (board.checkmate() ? "YES\n" : "NO\n");
    }
}

总结

这道题的主要难度在于debug。

AC代码

#include <iostream>
#include <vector>
using namespace std;
struct Piece {
    char name; int x, y;
    Piece(int a, int b): name{'G'}, x{a}, y{b} {}
    Piece(): name{}, x{}, y{} {
        cin >> name >> x >> y;
    }
    bool capture(Piece &position) {
        switch (name) {
            case 'G': return y == position.y; break;
            case 'C':
            case 'R': return x == position.x || y == position.y; break;
            case 'H': if (position.x == x + 1 || position.x == x - 1) {
                return position.y == y + 2 || position.y == y - 2;
            } else if (position.y == y + 1 || position.y == y - 1) {
                return position.x == x + 2 || position.x == x - 2;
            } break;
            default: cerr << "unreachable branch\n"; break;
        }
        return false;
    }
    bool block(Piece &from, Piece &to) {
        switch (from.name) {
            case 'G': if (from.y == to.y && from.y == y) {
                return (x > from.x && x < to.x) || (x < from.x && x > to.x);
            } break;
            case 'C':
            case 'R': if (from.x == to.x && from.x == x) {
                return (y > from.y && y < to.y) || (y < from.y && y > to.y);
            } else if (from.y == to.y && from.y == y) {
                return (x > from.x && x < to.x) || (x < from.x && x > to.x);
            } break;
            case 'H':
                if (from.y + 2 == to.y && from.y + 1 == y && from.x == x) return true;
                if (from.y - 2 == to.y && from.y - 1 == y && from.x == x) return true;
                if (from.x + 2 == to.x && from.x + 1 == x && from.y == y) return true;
                if (from.x - 2 == to.x && from.x - 1 == x && from.y == y) return true;
                break;
            default: cerr << "unreachable branch\n"; break;
        }
        return false;
    }
};
struct Board {
    int sz, x, y; vector<Piece> board;
    Board() = default;
    bool get() {
        board.clear(); sz = x = y = 0;
        cin >> sz >> x >> y;
        for (int i = 0; i < sz; ++i) {
            Piece p; board.push_back(p);
        }
        return sz != 0 || x != 0 || y != 0;
    }
    Piece position{x,y}; Piece &pos(int a, int b) {
        position.x = a; position.y = b;
        return position;
    }
    bool checkmate() {
        return bad_move(pos(x-1, y)) && bad_move(pos(x+1, y))
            && bad_move(pos(x, y-1)) && bad_move(pos(x, y+1));
    }
    int blocked(Piece &from, Piece &to) {
        int cnt{0}; for (Piece &p : board) {
            if (p.block(from, to)) ++cnt;
        }
        return cnt;
    }
    bool bad_move(Piece &m) {
        if (m.x < 1 || m.x > 3 || m.y < 4 || m.y > 6) return true;
        for (Piece &p : board) if (m.x != p.x || m.y != p.y) {
            if (p.capture(m)) {
                if (blocked(p, m) == (p.name == 'C' ? 1 : 0)) return true;
            }
        }
        return false;
    }
};
int main() {
    Board board;
    while (board.get()) {
        cout << (board.checkmate() ? "YES\n" : "NO\n");
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,869评论 2 9
  • 不论年龄几何,都保存一份童心童趣。
    静的花田阅读 384评论 0 3
  • 今天是个值得纪念的日子,上个月的今天我决定开始减肥,到今天为止已经整整一个月了,一个月瘦了15斤。现在的我已经...
    你今天怎么样阅读 301评论 0 0
  • 我看了《鼠来宝》这部电影,里面有三只花甲鼠分别叫:艾尔文、西蒙、 西奥多。 西蒙很爱吃,他是最胖的一只,别人要一份...
    蓝色之冥阅读 1,846评论 0 3