欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 艺术 > (C++回溯算法)微信小程序“开局托儿所”游戏

(C++回溯算法)微信小程序“开局托儿所”游戏

2025/2/22 16:59:15 来源:https://blog.csdn.net/m0_61219098/article/details/143490961  浏览:    关键词:(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=1mj=1naij被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) {}
};
// 顺序求解,从左上角开始寻找解决方案
// A: 矩阵A数据,逐行排列
// m: 矩阵A行数
// n: 矩阵A列数
// 注意: m * n < 256
std::vector<Operate> sequentialSolve(int8_t *A, uint8_t m, uint8_t n);
// 逆序求解,从右下角开始寻找解决方案
// A: 矩阵A数据,逐行排列
// m: 矩阵A行数
// n: 矩阵A列数
// 注意: m * n < 256
std::vector<Operate> inverseSolve(int8_t *A, uint8_t m, uint8_t n);
#endif

nursery_game.cpp

#include "nursery_game.h"
#include <utility>
using std::vector;
using std::move;// #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(move(set));goto F_push;}tag:x2++;}vector<Operate> sequentialSolve(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 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;
}vector<Operate> inverseSolve(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, m1 = m - 1, n1 = n - 1;
F_push:if (!unRemoveCount) {
#ifdef RECOVER_DATAint8_t *p = A + m * n;do {p--;*p = -*p;} while (p != A);
#endifreturn move(R);}is = m * n;for (uint8_t x1 = m1; x1 >= 0; x1--) {is -= n;for (uint8_t y1 = n1; y1 >= 0; 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 >= 0; y1--) handle(op.x1, y1, op.x1, F3)for (uint8_t x1 = op.x1 - 1; x1 >= 0; x1--) {is -= n;for (uint8_t y1 = n1; y1 >= 0; y1--) handle(x1, y1, x1, F4)}goto F_pop;
}#undef handle

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(sequentialSolve(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)

操作过程:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

按数字大小求解

仔细观察该游戏,我们容易发现往往大数更难以消除,因此我们可以优先消除大数,这样相对来说更容易找到解决方案。

代码

nursery_game.h

#ifndef NURSERY_GAME
#define NURSERY_GAME// ……
// 固定顺序代码// 按数字大小的顺序求解
// A: 矩阵A数据,逐行排列
// m: 矩阵A行数
// n: 矩阵A列数
// 注意: m, n < 17
std::vector<Operate> solve(int8_t *A, uint8_t m, uint8_t n);
#endif

nursery_game.cpp

// ……
// 固定顺序代码#include <unordered_set>
using std::unordered_set;struct PosNode {uint8_t pos, i, j;PosNode *next, *pre;PosNode():next(nullptr), pre(nullptr) {}PosNode(uint8_t p, uint8_t x, uint8_t y, PosNode *parent):pos(p), i(x), j(y), next(nullptr), pre(parent) {}
};#define Handle(nodeStart,num,tag) for(PosNode*node1=nodeStart;node1;){uint8_t i1,i2,j1,j2;if(i>node1->i){i1=node1->i;i2=i;}else{i1=i;i2=node1->i;}if(j>node1->j){j1=node1->j;j2=j;}else{j1=j;j2=node1->j;}uint16_t key=i1<<12|j1<<8|i2<<4|j2;if(calculated.find(key)==calculated.end()&&pushed->find(key)==pushed->end()){calculated.insert(key);vector<uint8_t>posSet;uint8_t k=0;for(uint8_t i=i1*n,ie=i2*n;i<=ie;i+=n)for(uint8_t j=i+j1,e=i+j2;j<=e;j++){int8_t a=A[j];if(a>0){if((k+=a)>10)goto tag;posSet.push_back(j);}}if(k<10)continue;R.emplace_back(i1,j1,i2,j2);for(auto pos:posSet){A[pos]=-A[pos];node=posToNode[pos];if(!(node->pre->next=node->next))posesEnd[pos]=node->pre;}count-=posSet.size();history.push_back(move(posSet));pushed->insert(key);historyPushed.push_back(pushed);pushed=new unordered_set<uint16_t>;goto F_push;}tag:node1=node1->next;}
#define handle(num,tag) Handle(poses[num]->next,num,tag)
#define handleSame(num,tag) Handle(node->next,num,tag)vector<Operate> solve(int8_t *A, uint8_t m, uint8_t n) {uint8_t N = m * n, count = N;PosNode *poses[10], *posesEnd[10], **posToNode = new PosNode * [N];for (uint8_t i = 1; i < 10; i++) poses[i] = posesEnd[i] = new PosNode;// 在这里遍历的时候还可以判断所有数字的总和是否被10整除,若不然可直接返回。// 因为在游戏中不存在不整除的情况,所以没加上这个判断条件。for (uint8_t i = 0, p = 0; i < m; i++)for (uint8_t j = 0; j < n; j++, p++) {PosNode *&node = posesEnd[A[p]];posToNode[p] = node = node->next = new PosNode(p, i, j, node);}vector<Operate> R;vector<vector<uint8_t>> history;unordered_set<uint16_t> calculated;vector<unordered_set<uint16_t> *> historyPushed;unordered_set<uint16_t> *pushed = new unordered_set<uint16_t>;
F_push:if (!count) {PosNode **p = posToNode + N;do delete *--p; while (p != posToNode);delete[] posToNode;p = poses + 9;do delete *p; while (--p != poses);delete pushed;for (auto p : historyPushed) delete p;
#ifdef RECOVER_DATAint8_t *q = A + N;do {q--;*q = -*q;} while (q != A);
#endifreturn move(R);}calculated.clear();for (PosNode *node = poses[9]->next; node; node = node->next) {uint8_t i = node->i, j = node->j;handle(1, F1)}for (PosNode *node = poses[8]->next; node; node = node->next) {uint8_t i = node->i, j = node->j;handle(2, F2)handle(1, F3)}for (PosNode *node = poses[7]->next; node; node = node->next) {uint8_t i = node->i, j = node->j;handle(3, F4)handle(2, F5)handle(1, F6)}for (PosNode *node = poses[6]->next; node; node = node->next) {uint8_t i = node->i, j = node->j;handle(4, F7)handle(3, F8)handle(2, F9)handle(1, F10)}for (PosNode *node = poses[5]->next; node; node = node->next) {uint8_t i = node->i, j = node->j;handleSame(5, F11)handle(4, F12)handle(3, F13)handle(2, F14)handle(1, F15)}for (PosNode *node = poses[4]->next; node; node = node->next) {uint8_t i = node->i, j = node->j;handleSame(4, F16)handle(3, F17)handle(2, F18)handle(1, F19)}for (PosNode *node = poses[3]->next; node; node = node->next) {uint8_t i = node->i, j = node->j;handleSame(3, F20)handle(2, F21)handle(1, F22)}for (PosNode *node = poses[2]->next; node; node = node->next) {uint8_t i = node->i, j = node->j;handleSame(2, F23)handle(1, F24)}for (PosNode *node = poses[1]->next; node; node = node->next) {uint8_t i = node->i, j = node->j;handleSame(1, F25)}if (R.empty()) {PosNode **p = posToNode + N;do delete *--p; while (p != posToNode);delete[] posToNode;p = poses + 9;do delete *p; while (--p != poses);delete pushed;for (auto p : historyPushed) delete p;return {};}R.pop_back();count += history.back().size();for (auto pos : history.back()) {PosNode *node = posToNode[pos], *&nodeEnd = posesEnd[A[pos] = -A[pos]];node->next = nullptr;node->pre = nodeEnd;nodeEnd = nodeEnd->next = node;}history.pop_back();delete pushed;pushed = historyPushed.back();historyPushed.pop_back();goto F_push;
}

test.cpp

#include "nursery_game.h"
#include <stdio.h>
using namespace std;int main() {int8_t data[] = { 1,2,1,6,5,7,5,1,7,2,6,1,1,8,2,1,9,1,1,4,4,2,4,1,2,3,7,3,3,4,2,1,2,9,1,7,4,4,2,2,4,2,4,8,4,7,1,1,2,1,1,1,1,4,4,1,4,4,1,1,7,1,7,1 };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;
}

测试结果

(2,0) (2,1)
(4,1) (4,2)
(1,5) (1,6)
(5,3) (7,3)
(3,1) (3,2)
(3,3) (4,3)
(4,3) (4,6)
(6,2) (7,4)
(0,0) (0,3)
(0,4) (2,4)
(2,3) (2,6)
(1,1) (4,3)
(2,5) (4,7)
(0,3) (3,5)
(1,0) (3,7)
(0,6) (6,6)
(4,6) (7,7)
(6,1) (7,6)
(4,1) (6,4)
(5,0) (7,0)
(0,0) (7,7)

操作过程:

0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
G
H
I
J
K
而经过测试,无论是顺序遍历还是逆序遍历在此案例中均耗时较长,但按元素大小遍历并非最优求解算法,如:

A = ( 2 3 2 3 2 1 7 1 6 2 2 4 1 2 3 9 4 2 1 3 3 2 3 2 1 7 7 3 4 2 2 2 2 1 7 2 1 8 1 1 2 5 5 3 4 3 6 3 8 9 1 4 2 4 6 2 2 3 4 1 8 1 1 2 ) \bm A=\begin{pmatrix}2&3&2&3&2&1&7&1\\6&2&2&4&1&2&3&9\\4&2&1&3&3&2&3&2\\1&7&7&3&4&2&2&2\\2&1&7&2&1&8&1&1\\2&5&5&3&4&3&6&3\\8&9&1&4&2&4&6&2\\2&3&4&1&8&1&1&2\end{pmatrix} A= 2641228232271593221775143433234121341428122283417332166119221322

此时,顺序遍历比按大小遍历耗时更短。因此选择求解方法时需要根据具体案例选择不同的方法,具体而言可通过多线程等方式实现最优的求解时间。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词