高级农民
- 积分
- 2716
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2017-6-18
- 最后登录
- 1970-1-1
|
本帖最后由 magicsets 于 2018-1-19 23:33 编辑
第二个题目确实比较复杂,这种工程上的复杂性可以通过抽象、分解模块并封装来处理
我试着写了一下,主要定义了5个类:
Point, Rectangle, Tile, Canvas, TileManager
其中Point和Rectangle用于服务坐标计算以及对拼图边界的处理
Tile表示一个2X2的砖片,支持旋转操作
Canvas用于抽象一个“无边界”,支持负数坐标的拼图
在实现了以上基础设施的情况下,再实现拼图算法,结构和代码就比较清晰了- #include <array>
- #include <cstddef>
- #include <cstring>
- #include <iostream>
- #include <memory>
- #include <stdexcept>
- #include <string>
- #include <unordered_set>
- #include <vector>
- #define DISALLOW_COPY_AND_ASSIGN(classname) \
- classname(const classname &other) = delete; \
- classname& operator=(const classname &other) = delete
- struct Point {
- int x, y;
- Point shift(const Point &delta) const {
- return { x + delta.x, y + delta.y };
- }
- Point operator-() const {
- return { -x, -y };
- }
- bool operator==(const Point &other) const {
- return x == other.x && y == other.y;
- }
- };
- struct PointHasher {
- std::size_t operator()(const Point &loc) const {
- return (static_cast<std::size_t>(loc.x) << 32) | static_cast<std::size_t>(loc.y);
- }
- };
- struct Rectangle {
- int left, right, top, bottom;
- int width() const {
- return right - left + 1;
- }
- int area() const {
- return (right - left + 1) * (bottom - top + 1);
- }
- bool contains(const Point &loc) const {
- return loc.x >= left && loc.x <= right && loc.y >= top && loc.y <= bottom;
- }
- int address(const int x, const int y) const {
- return x - left + (y - top) * width();
- }
- Rectangle intersect(const Rectangle &other) const {
- return { std::max(left, other.left),
- std::min(right, other.right),
- std::max(top, other.top),
- std::min(bottom, other.bottom) };
- }
- Rectangle extend(const Rectangle &other) const {
- return { std::min(left, other.left),
- std::max(right, other.right),
- std::min(top, other.top),
- std::max(bottom, other.bottom) };
- }
- };
- class Canvas {
- public:
- Canvas(const Rectangle &frame, const char blank)
- : frame_(frame), blank_(blank),
- cells_(static_cast<char*>(std::malloc(frame_.area()))) {
- std::memset(cells_, blank_, frame_.area());
- }
- ~Canvas() {
- std::free(cells_);
- }
- char& operator[](const Point &loc) {
- if (!frame_.contains(loc)) {
- const Rectangle nframe = {
- Extend(loc.x, frame_.left), Extend(loc.x, frame_.right),
- Extend(loc.y, frame_.top), Extend(loc.y, frame_.bottom)
- };
- resize(nframe);
- }
- return cells_[frame_.address(loc.x, loc.y)];
- }
- std::string toString(const Rectangle &box) const {
- const Rectangle region = frame_.intersect(box);
- std::string out;
- for (int y = region.top; y <= region.bottom; ++y) {
- out.append(cells_ + frame_.address(region.left, y), region.width());
- out.push_back('\n');
- }
- return out;
- }
- private:
- static int Extend(const int value, int bound) {
- if (bound < 0) {
- while (bound > value) {
- bound *= 2;
- }
- } else {
- while (bound < value) {
- bound *= 2;
- }
- }
- return bound;
- }
- void resize(const Rectangle &nframe) {
- char *ncells = static_cast<char*>(std::malloc(nframe.area()));
- std::memset(ncells, blank_, nframe.area());
- for (int y = frame_.top; y <= frame_.bottom; ++y) {
- std::memcpy(ncells + nframe.address(frame_.left, y),
- cells_ + frame_.address(frame_.left, y),
- frame_.width());
- }
- std::free(cells_);
- cells_ = ncells;
- frame_ = nframe;
- }
- Rectangle frame_;
- const char blank_;
- char *cells_;
- DISALLOW_COPY_AND_ASSIGN(Canvas);
- };
- class Tile {
- public:
- Tile(const char lu, const char ru, const char rb, const char lb)
- : cells_({{lu, ru, rb, lb}}) {
- }
- Tile(const Tile &other) {
- std::memcpy(cells_.data(), other.cells_.data(), sizeof(cells_));
- }
- Tile rotate90() const {
- return Tile(cells_[3], cells_[0], cells_[1], cells_[2]);
- }
- char operator[](const int corner) const {
- return cells_[corner];
- }
- private:
- // 0 1
- // XX
- // XX
- // 3 2
- std::array<char, 4> cells_;
- };
- class TileManager {
- public:
- TileManager()
- : canvas_({-8, 8, -8, 8}, ' '),
- shadow_({-8, 8, -8, 8}, kEmptyCell),
- bounds_({0, 0, 0, 0}) {
- }
- void link(const char **tiles) {
- std::vector<Tile> input;
- while (true) {
- input.emplace_back(tiles[0][0], tiles[0][1], tiles[1][1], tiles[1][0]);
- if (tiles[2] == 0) {
- break;
- }
- tiles += 3;
- }
- std::cout << input.size() << " " << "tiles\n";
- for (int i = 0; i < input.size(); ++i) {
- link(input[i]);
- std::cout << "link " << (i + 1) << "\n";
- std::cout << canvas_.toString(bounds_) << "\n";
- }
- }
- private:
- void link(Tile tile) {
- const Point loc = locate(tile);
- for (int dir = 0; dir < 4; ++dir) {
- const Point cell = loc.shift(kTileOffsets[dir]);
- slots_.erase(cell);
- const char ch = tile[dir];
- canvas_[cell] = ch;
- shadow_[cell] = kInvalidCell;
- for (int i = 0; i < 2; ++i) {
- const Point slot = cell.shift(kSlotOffsets[dir * 2 + i]);
- char &orig = shadow_[slot];
- if (orig == kEmptyCell) {
- orig = ch;
- slots_.emplace(slot);
- } else if (orig != kInvalidCell && orig != ch) {
- orig = kInvalidCell;
- slots_.erase(slot);
- }
- }
- }
- bounds_ = bounds_.extend({loc.x, loc.x + 1, loc.y, loc.y + 1});
- }
- Point locate(Tile &target) {
- if (slots_.empty()) {
- return {0, 0};
- }
- for (const Point &slot : slots_) {
- for (const Point &offset : kTileOffsets) {
- const Point loc = slot.shift(-offset);
- for (int rotate = 0; rotate < 4; ++rotate) {
- target = target.rotate90();
- if (matches(target, loc)) {
- return loc;
- }
- }
- }
- }
- throw std::runtime_error("Invalid input!");
- }
- bool matches(const Tile &tile, const Point &loc) {
- int count = 0;
- for (int dir = 0; dir < 4; ++dir) {
- const char expected = shadow_[loc.shift(kTileOffsets[dir])];
- if (expected == kEmptyCell) {
- continue;
- }
- if (expected != tile[dir]) {
- return false;
- }
- ++count;
- }
- return count >= 2;
- }
- Canvas canvas_;
- Canvas shadow_;
- Rectangle bounds_;
- std::unordered_set<Point, PointHasher> slots_;
- constexpr static char kEmptyCell = '.';
- constexpr static char kInvalidCell = 'X';
- const static std::array<Point, 4> kTileOffsets;
- const static std::array<Point, 8> kSlotOffsets;
- DISALLOW_COPY_AND_ASSIGN(TileManager);
- };
- const std::array<Point, 4> TileManager::kTileOffsets = {{
- { 0, 0 }, { 1, 0 }, { 1, 1 }, { 0, 1 }
- }};
- const std::array<Point, 8> TileManager::kSlotOffsets = {{
- { -1, 0 }, { 0, -1 },
- { 0, -1 }, { 1, 0 },
- { 1, 0 }, { 0, 1},
- { 0, 1 }, { -1, 0 }
- }};
- int main(int argc, char *argv[]) {
- std::unique_ptr<TileManager> tm;
- tm = std::make_unique<TileManager>();
- const char *tiles1[] = {
- "11", // 1
- "23",
- "",
- "44", // 2
- "11",
- "",
- "16", // 3
- "36",
- "",
- "51", // 4
- "71",
- "",
- "46", // 5
- "26",
- "",
- "14", // 5
- "68",
- 0
- };
- tm->link(tiles1);
- std::cout << "--------\n\n";
- tm = std::make_unique<TileManager>();
- const char *tiles2[] = {
- "12", // 1
- "34",
- "",
- "36", // 2
- "15",
- "",
- "56", // 3
- "78",
- "",
- "78", // 4
- "90",
- "",
- "46", // 5
- "26",
- "",
- "88", // 6
- "01",
- "",
- "93", // 7
- "54",
- 0
- };
- tm->link(tiles2);
- std::cout << "--------\n\n";
- tm = std::make_unique<TileManager>();
- const char *tiles3[] = {
- "11", // 1
- "12",
- "",
- "12", // 2
- "93",
- "",
- "93", // 3
- "99",
- "",
- "39", // 4
- "98",
- "",
- "95", // 5
- "86",
- "",
- "57", // 6
- "66",
- "",
- "77", // 7
- "57",
- "",
- "13", // 8
- "24",
- "",
- "45", // 9
- "47",
- "",
- "24", // 10
- "39",
- 0
- };
- tm->link(tiles3);
- return 0;
- }
复制代码 |
|