当前位置: 首页 > news >正文

【BFS】P7555 [USACO21OPEN] Maze Tac Toe S|普及+

本文涉及知识点

C++BFS算法

P7555 [USACO21OPEN] Maze Tac Toe S

题目描述

奶牛 Bessie 喜欢玩走迷宫。她同样喜欢玩井字棋(更准确地说,奶牛版的井字棋,马上会进行介绍)。Farmer John 找到了一种全新的方式,可以使她同时玩到这两种游戏!

首先,奶牛井字棋:与在 3×33 \times 33×3 棋盘内放置 XO 不同,奶牛当然是在 3×33 \times 33×3 棋盘内放置 MO。在一方的回合内,玩家可以选择将一个 M 或一个 O 放在任意一个空格内(这是另一个与标准井字棋的不同之处,标准的井字棋中一名玩家始终放 X 而另一名玩家始终放 O)。奶牛井字棋的胜利方是首位拼出 MOO 的玩家,可以是同行、同列或对角线。倒着拼出也是可以的,例如一名玩家在棋盘的一行内拼出 OOM 也可以获胜。如同标准井字棋一样,有可能最后没有玩家取得胜利。奶牛井字棋的一次行动通常用 3 个字符表示,MijOij,其中 iiijjj 在范围 1…31 \ldots 313 之间,表示放置 MO 的行与列。

为了给 Bessie 增加一些挑战,Farmer John 设计了一个由 N×NN \times NN×N 个方格组成的正方形迷宫(3≤N≤253 \leq N \leq 253N25)。某些方格,包括所有的边界方格,有巨大的草堆,使得 Bessie 不能移动到这些方格。Bessie 可以沿东南西北四个方向在迷宫内的所有其他方格内自由行动。某些方格内有一张纸,上面写有奶牛井字棋的一次行动。当 Bessie 在迷宫中移动时,任何时刻她停留在这样的方格上,她都必须于迷宫中移动之时在同时进行的奶牛井字棋游戏内做出对应的行动(除非奶牛井字棋中对应的方格已经被填上了,在这种情况下她不进行行动)。在奶牛井字棋游戏内没有与她对阵的玩家,但迷宫中的某些方格可能会不利于她最终拼出 MOO

如果 Bessie 在获胜之时就会立刻停止奶牛井字棋,请求出她可以通过适当的方式在迷宫中移动而完成的不同的胜利状态棋盘数量。

输入格式

输入的第一行包含 NNN

以下 NNN 行,每行包含 3N3N3N 个字符,描述迷宫。每个方格用 3 个字符表示,### 代表墙,... 代表空格,BBB 代表 Bessie 所在的一个非墙方格,或者一个奶牛井字棋的行动,表示 Bessie 必须进行对应行动的一个非墙方格。恰好一个方格为 BBB

输出格式

输出 Bessie 可以通过在迷宫中行动并在获胜时立刻停止而产生的不同的胜利状态下的奶牛井字棋盘数量(可能为 000)。

输入输出样例 #1

输入 #1

7
#####################
###O11###...###M13###
###......O22......###
###...######M22######
###BBB###M31###M11###
###...O32...M33O31###
#####################

输出 #1

8

说明/提示

样例说明

在这个样例中,Bessie 最终可能达成 888 种胜利的棋盘状态:

O.M
.O.
MOMO..
.O.
.OMO.M
.O.
.OMO..
.O.
MOMO..
...
OOM..M
.O.
OOM...
.O.
OOM...
...
OOM

对其中一种情况进行说明:

O..
...
OOM

在这里,Bessie 可以先移动到 O11 方格,然后移动到下方并通过 O32M33O31。此时游戏结束,因为她获得了胜利(例如她不能再前往位于她当前所在的 O31 方格北面的 M11 方格)。

说明

供题:Brian Dean

BFS

状态:当前位置(行列),棋盘样式。 前者数量:N*N,后者数量393^939。总数量:5×1055\times 10^55×105
临接矩阵:4临接 且 不是墙。
状态转移:位置一定发生变化,棋盘可能发生变化。当前状态是胜利状态,则无后续状态。
初始状态:枚举各非墙单格,并执行此单格的事件。
出重:vis数组。
最终结果:枚举所有胜利棋盘状态,vis[棋盘状态][任意行][任意列]为真,则ans+。

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>
#include<array>#include <bitset>
using namespace std;template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {in >> pr.first >> pr.second;return in;
}template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t);return in;
}template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);return in;
}template<class T = int>
vector<T> Read() {int n;cin >> n;vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}
template<class T = int>
vector<T> ReadNotNum() {vector<T> ret;T tmp;while (cin >> tmp) {ret.emplace_back(tmp);if ('\n' == cin.get()) { break; }}return ret;
}template<class T = int>
vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}return ret;
}template<int N = 1'000'000>
class COutBuff
{
public:COutBuff() {m_p = puffer;}template<class T>void write(T x) {int num[28], sp = 0;if (x < 0)*m_p++ = '-', x = -x;if (!x)*m_p++ = 48;while (x)num[++sp] = x % 10, x /= 10;while (sp)*m_p++ = num[sp--] + 48;AuotToFile();}void writestr(const char* sz) {strcpy(m_p, sz);m_p += strlen(sz);AuotToFile();}inline void write(char ch){*m_p++ = ch;AuotToFile();}inline void ToFile() {fwrite(puffer, 1, m_p - puffer, stdout);m_p = puffer;}~COutBuff() {ToFile();}
private:inline void AuotToFile() {if (m_p - puffer > N - 100) {ToFile();}}char  puffer[N], * m_p;
};template<int N = 1'000'000>
class CInBuff
{
public:inline CInBuff() {}inline CInBuff<N>& operator>>(char& ch) {FileToBuf();ch = *S++;return *this;}inline CInBuff<N>& operator>>(int& val) {FileToBuf();int x(0), f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行		return *this;}inline CInBuff& operator>>(long long& val) {FileToBuf();long long x(0); int f(0);while (!isdigit(*S))f |= (*S++ == '-');while (isdigit(*S))x = (x << 1) + (x << 3) + (*S++ ^ 48);val = f ? -x : x; S++;//忽略空格换行return *this;}template<class T1, class T2>inline CInBuff& operator>>(pair<T1, T2>& val) {*this >> val.first >> val.second;return *this;}template<class T1, class T2, class T3>inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val);return *this;}template<class T1, class T2, class T3, class T4>inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);return *this;}template<class T = int>inline CInBuff& operator>>(vector<T>& val) {int n;*this >> n;val.resize(n);for (int i = 0; i < n; i++) {*this >> val[i];}return *this;}template<class T = int>vector<T> Read(int n) {vector<T> ret(n);for (int i = 0; i < n; i++) {*this >> ret[i];}return ret;}template<class T = int>vector<T> Read() {vector<T> ret;*this >> ret;return ret;}
private:inline void FileToBuf() {const int canRead = m_iWritePos - (S - buffer);if (canRead >= 100) { return; }if (m_bFinish) { return; }for (int i = 0; i < canRead; i++){buffer[i] = S[i];//memcpy出错			}m_iWritePos = canRead;buffer[m_iWritePos] = 0;S = buffer;int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);if (readCnt <= 0) { m_bFinish = true; return; }m_iWritePos += readCnt;buffer[m_iWritePos] = 0;S = buffer;}int m_iWritePos = 0; bool m_bFinish = false;char buffer[N + 10], * S = buffer;
};class CGrid {
public:CGrid(int rCount, int cCount) :m_r(rCount), m_c(cCount) {}template<class Fun1, class Fun2>vector<vector<pair<int, int>>> NeiBo(Fun1 funVilidCur, Fun2 funVilidNext, int iConnect = 4){vector<vector<pair<int, int>>> vNeiBo(m_r * m_c);for (int r = 0; r < m_r; r++){for (int c = 0; c < m_c; c++){if (!funVilidCur(r, c))continue;auto& v = vNeiBo[Mask(r, c)];if ((r > 0) && funVilidNext(r - 1, c)) {v.emplace_back(r - 1, c);}if ((c > 0) && funVilidNext(r, c - 1)) {v.emplace_back(r, c - 1);}if ((r + 1 < m_r) && funVilidNext(r + 1, c)) {v.emplace_back(r + 1, c);}if ((c + 1 < m_c) && funVilidNext(r, c + 1)) {v.emplace_back(r, c + 1);}}}return vNeiBo;}vector<vector<int>> Dis(vector<pair<int, int>> start, std::function<bool(int, int)> funVilidCur, std::function<bool(int, int)> funVilidNext, const int& iConnect = 4) {static short dir[8][2] = { {0, 1}, {1, 0}, {-1, 0},{ 0, -1},{1,1},{1,-1},{-1,1},{-1,-1} };vector<vector<int>> vDis(m_r, vector<int>(m_c, -1));for (const auto& [r, c] : start) {vDis[r][c] = 0;}for (int i = 0; i < start.size(); i++) {const auto [r, c] = start[i];if (!funVilidCur(r, c)) { continue; }for (int k = 0; k < iConnect; k++) {const int r1 = r + dir[k][0];const int c1 = c + dir[k][1];if ((r1 < 0) || (r1 >= m_r) || (c1 < 0) || (c1 >= m_c)) { continue; }if (funVilidNext(r1, c1) && (-1 == vDis[r1][c1])) {start.emplace_back(r1, c1);vDis[r1][c1] = vDis[r][c] + 1;}}}return vDis;}void EnumGrid(std::function<void(int, int)> call){for (int r = 0; r < m_r; r++){for (int c = 0; c < m_c; c++){call(r, c);}}}vector<pair<int, int>> GetPos(std::function<bool(int, int)> fun) {vector<pair<int, int>> ret;for (int r = 0; r < m_r; r++){for (int c = 0; c < m_c; c++){if (fun(r, c)) {ret.emplace_back(r, c);}}}return ret;}inline int Mask(int r, int c) { return  m_c * r + c; }const int m_r, m_c;const inline static int s_Moves[][2] = { {1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1} };
};class Solution {
public:int Ans(vector<string>& mat) {const int N = mat.size();int MMC = 1;for (int i = 0; i < 9; i++) { MMC *= 3; }const int MC = MMC * N * N;CGrid grid(N, N);auto Vilid = [&](int r, int c) {return "###" != mat[r].substr(3 * c, 3);};auto neiBo = grid.NeiBo(Vilid, Vilid);vector<int> suc(MMC);vector<array<array<int, 3>, 3>> maskToMaze(MMC);{for (int mask = 0; mask < MMC; mask++){auto& maze = maskToMaze[mask];auto tmp = mask;for (int r = 2; r >= 0; r--)for (int c = 2; c >= 0; c--) {maze[r][c] = tmp % 3;tmp /= 3;}suc[mask] = IsSuc(maze);}}vector<bool> vis(MC);queue<tuple<int, int, int>> que;auto Add = [&](int MMask, int r, int c) {const int m = N * N * MMask + N * r + c;if (vis[m]) { return; }vis[m] = true;que.emplace(MMask, r, c);if (suc[MMask]) { suc[MMask] = 2; }};auto Do = [&](int PreMMask, int r, int c) {string str = mat[r].substr(3 * c, 3);if ("###" == str) { return; }else if ("..." == str) { Add(PreMMask, r, c); }else if ("BBB" == str) { Add(PreMMask, r, c); }else {const int mr = str[1] - '1';const int mc = str[2] - '1';int MMask = PreMMask;if (0 == maskToMaze[PreMMask][mr][mc]) {int add = ('M' == str[0]) ? 2 : 1;MMask += Unit(mr, mc) * add;}Add(MMask, r, c);}};for (int r = 0; r < N; r++)for (int c = 0; c < N; c++) {string str = mat[r].substr(3 * c, 3);if ("BBB" != str) { continue; }Do(0, r, c);}while (que.size()) {const auto [MM, r, c] = que.front(); que.pop();if (suc[MM] > 1) { continue; }for (const auto& [r1, c1] : neiBo[N * r + c]) {Do(MM, r1, c1);}}return count(suc.begin(), suc.end(), 2);}bool IsSuc(int x, int y, int z) { return IsSuc1(x, y, z) || IsSuc1(z, y, x); }bool IsSuc1(int x, int y, int z) {return (2 == x) && (1 == y) && (1 == z);}bool IsSuc(array<array<int, 3>, 3>& maze) {if (IsSuc(maze[0][0], maze[1][1], maze[2][2])) { return true; }//对角线if (IsSuc(maze[0][2], maze[1][1], maze[2][0])) { return true; }//反对角线for (int rc = 0; rc < 3; rc++) {if (IsSuc(maze[rc][0], maze[rc][1], maze[rc][2])) { return true; }if (IsSuc(maze[0][rc], maze[1][rc], maze[2][rc])) { return true; }}return false;}int Unit(int r, int c) {int ret = 1;while (r < 2) { r++; ret *= 27; }while (c < 2) { c++, ret *= 3; }return ret;}
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUG	ios::sync_with_stdio(0); cin.tie(nullptr);//CInBuff<> in; COutBuff<10'000'000> ob;auto mat = Read<string>();
#ifdef _DEBUG		//printf("M=%d,a=%d,", M,a);Out(mat, "mat=");//Out(B, "B=");//Out(que, "que=");//Out(B, "B=");
#endif // DEBUG	auto res = Solution().Ans(mat);cout << res << "\n";//ob.write(res); ob.write('\n');return 0;
}

单元测试

int N;vector<string> mat;TEST_METHOD(TestMethod1){mat = { "#####################","###O11###...###M13###","###......O22......###","###...######M22######","###BBB###M31###M11###","###...O32...M33O31###","#####################" };auto res = Solution().Ans( mat);AssertEx(8, res);}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

http://www.dtcms.com/a/309665.html

相关文章:

  • Java向量化
  • C语言使用GmSSL库实现sm3、sm4算法
  • SH3001六轴传感器应用(二)(IIC驱动开发)
  • Linux---编辑器vim
  • JAVA结合AI
  • Linux 硬盘分区管理
  • 工程化(一):Vite vs. Webpack:从“打包”到“服务”,构建工具的范式转移
  • 鸿蒙系统下的动态负载均衡实战:让分布式任务调度更智能
  • 灵敏度,精度,精确度,精密度,精准度,准确度,分辨率,分辨力——概念
  • 谈谈对反射的理解?
  • AJAX 解析与高频问题
  • 在 MEF 中处理多个 Tab 页对应同一模块的不同实例
  • python进程、线程、协程
  • 第二十三天(数据结构:链表补充【希尔表】)
  • 海外商城 app 系统架构分析
  • 电子电气架构 --- 软件项目变更管理
  • Corrosion2靶机攻略
  • 第七章 愿景12 小萍分享《人性的弱点》
  • 大模型推理引擎总结
  • AIGC(生成式AI)试用 35 -- 用AI解析句子结构
  • 力扣-最大单词长度乘积
  • 守护数字核心:主机安全的重要性与全方位防护指南
  • .NET 如何实现ChatGPT的Stream传输
  • HCLP--ospf综合实验
  • Qt大数据监控平台
  • Ant Design Vue notification自定义
  • 算法题——数组
  • Master Prompt:AI时代的万能协作引擎
  • html5和vue区别
  • 使用 Scikit-LLM 进行零样本和少样本分类