(C++回溯算法)微信小程序“开局托儿所”游戏
问题描述
给定一个矩阵 A = ( a i j ) m × n \bm A=(a_{ij})_{m\times n} A=(aij)m×n,其中 a i j ∈ { 1 , 2 , ⋯ , 9 } a_{ij}\in\{1,2,\cdots,9\} aij∈{1,2,⋯,9},且满足 ∑ i = 1 m ∑ j = 1 n a i j \sum\limits_{i=1}^m\sum\limits_{j=1}^na_{ij} i=1∑mj=1∑naij被10整除。玩家每次操作需要选择 A \bm A A中某个所有非空元素之和为10的子矩阵,并将其中所有的元素都标记为空。求按何种顺序消除能将 A \bm A A中所有的元素都标记为空,若存在则返回该解决方案,否则返回空列表。
代码
nursery_game.h
#ifndef NURSERY_GAME
#define NURSERY_GAME
#include <vector>
#include <stdint.h>
struct Operate {uint8_t x1, y1, x2, y2;Operate() {}Operate(uint8_t i1, uint8_t j1, uint8_t i2, uint8_t j2):x1(i1), y1(j1), x2(i2), y2(j2) {}
};
std::vector<Operate> solve(int8_t *data, uint8_t m, uint8_t n);
#endif
nursery_game.cpp
#include "nursery_game.h"
#include <utility>
using std::vector;// #define RECOVER_DATA // 若希望不改变A的值请解开本行注释#define handle(x1,y1,x2_start,tag) for(uint8_t x2=x2_start;x2<m;){uint8_t ie=x2*n;for(uint8_t y2=y1;y2<n;y2++){uint8_t sum=0;for(uint8_t i=is;i<=ie;i+=n)for(uint8_t j=i+y1,e=i+y2;j<=e;j++){int8_t t=A[j];if(t>0&&(sum+=t)>10)goto tag;}if(sum!=10)continue;vector<uint8_t> set;for(uint8_t i=is;i<=ie;i+=n)for(uint8_t j=i+y1,e=i+y2;j<=e;j++){int8_t t=A[j];if(t>0){A[j]=-t;set.push_back(j);}}R.emplace_back(x1,y1,x2,y2);unRemoveCount-=set.size();posSet.push_back(std::move(set));goto F_push;}tag:x2++;}// A: 矩阵A数据,逐行排列
// m: 矩阵A行数
// n: 矩阵A列数
vector<Operate> solve(int8_t *A, uint8_t m, uint8_t n) {vector<Operate> R;vector<vector<uint8_t>> posSet;Operate op;uint8_t unRemoveCount = m * n, is;
F_push:if (!unRemoveCount) {
#ifdef RECOVER_DATAint8_t *p = A + m * n;do {--p;*p = -*p;} while (p != A);
#endifreturn std::move(R);}is = 0;for (uint8_t x1 = 0; x1 < m; x1++, is += n)for (uint8_t y1 = 0; y1 < n; y1++)handle(x1, y1, x1, F1)
F_pop:if (R.empty()) return {};op = R.back();R.pop_back();unRemoveCount += posSet.back().size();for (auto pos : posSet.back()) A[pos] = -A[pos];posSet.pop_back();is = op.x1 * n;handle(op.x1, op.y1, op.x2 + 1, F2)for (uint8_t y1 = op.y1 + 1; y1 < n; y1++) handle(op.x1, y1, op.x1, F3)for (uint8_t x1 = op.x1 + 1; x1 < m; x1++) {is += n;for (uint8_t y1 = 0; y1 < n; y1++) handle(x1, y1, x1, F4)}goto F_pop;
}
test.cpp
#include "nursery_game.h"
#include <stdio.h>
using namespace std;int main() {int8_t data[] = { 4,7,3,3,6,5,4,4,2,1,8,4,2,2,2,6,1,2,3,2,3,2,7,1,2,8,1,3,1,6,4,5,4,5,1,4,2,2,2,3,8,3,3,1,9,2,3,3,1,1,4,4,1,9,3,7,1,3,2,5,3,1,1,5 };vector<Operate> r(solve(data, 8, 8));for (auto op : r) printf("(%d,%d) (%d,%d)\n", op.x1, op.y1, op.x2, op.y2);return 0;
}
测试结果
(0,1) (0,2)
(0,0) (2,1)
(0,0) (3,1)
(0,7) (1,7)
(1,3) (1,6)
(0,4) (3,4)
(1,3) (5,3)
(0,3) (2,5)
(0,3) (4,5)
(0,4) (6,4)
(2,0) (2,6)
(0,2) (4,2)
(0,2) (4,6)
(5,0) (7,0)
(5,7) (6,7)
(6,0) (7,2)
(5,0) (6,3)
(0,1) (5,6)
(0,5) (7,5)
(4,0) (6,7)
(3,7) (7,7)
(0,0) (7,7)
操作过程: