> 技术文档 > 【图论 BFS染色 并集查找 】P3663 [USACO17FEB] Why Did the Cow Cross the Road III S|普及+_p3659 [usaco17feb] why did the cow cross the road

【图论 BFS染色 并集查找 】P3663 [USACO17FEB] Why Did the Cow Cross the Road III S|普及+_p3659 [usaco17feb] why did the cow cross the road


本文涉及知识点

C++图论
C++并集查找 预计2025年5月29号 7:00发布
C++BFS算法

P3663 [USACO17FEB] Why Did the Cow Cross the Road III S

题目描述

奶牛为什么要过马路?其中一个原因是 Farmer John 的农场有很多道路,使得他的奶牛在四处走动时不可避免地要穿过许多道路。

FJ 的农场被安排成一个 N×N N \\times N N×N 的方形网格田地 2≤N≤100 2 \\leq N \\leq 100 2N100),某些相邻的田地(例如南北向或东西向)被道路分隔,整个网格的外部有一圈高高的围栏,防止奶牛离开农场。奶牛可以从任何田地自由移动到相邻的田地(北、东、南或西),尽管它们除非绝对必要,否则不愿意穿过道路。

农场上有 K K K 头奶牛( 1≤K≤100,K≤ N 2 1 \\leq K \\leq 100, K \\leq N^2 1K100,KN2),每头奶牛位于不同的田地。如果一头奶牛要拜访另一头奶牛时必须至少穿过一条道路,那么这对奶牛被称为“远距离”对。请帮助 FJ 计算远距离奶牛对的数量。

输入格式

输入的第一行包含 N N N K K K R R R。接下来的 R R R 行描述了 R R R 条存在于相邻田地之间的道路。每行的格式为 r r r c c c r ′ r\' r c ′ c\' c(范围为 1…N 1 \\ldots N 1N 的整数),表示位于(行 r r r,列 c c c)的田地与相邻的(行 r ′ r\' r,列 c ′ c\' c)的田地之间有一条道路。最后的 K K K 行表示 K K K 头奶牛的位置,每行用行和列指定。

输出格式

输出远距离奶牛对的数量。

输入输出样例 #1

输入 #1

3 3 32 2 2 33 3 3 23 3 2 33 32 22 3

输出 #1

2

并集查找

本题   ⟺    \\iff 4连接的单格连通,除非2个单格间有路。求在不连通区域的奶牛对数量。
编码(二维转一维)Mask(r,c) {return rC+c;}
not[mask]记录不连通的4连接单格,如果mask和mask1不连通,则not[mask].push_back(mask1)
not[mask1].push_back(mask)
neiBo[mask]记录mask的临接点,4连接除not外的数据。
利用nieBo并集查找各节点所属联通区域,计算各连通区域奶牛数量。
枚举各联通区域奶牛数量x
ans +=x
(K-x)
最终结果ans/2。
注意:也可以用BFS染色法求联通区域。

代码

核心代码

#include #include #include #include#include#include#include#include#include#include#include#include #include#include#include #include #include#include#include#include 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;scanf(\"%d\", &n);vector<T> ret(n);for (int i = 0; i < n; i++) {cin >> ret[i];}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;}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 CUnionFind{public:CUnionFind(int iSize) :m_vNodeToRegion(iSize){for (int i = 0; i < iSize; i++){m_vNodeToRegion[i] = i;}m_iConnetRegionCount = iSize;}CUnionFind(vector<vector<int>>& vNeiBo) :CUnionFind(vNeiBo.size()){for (int i = 0; i < vNeiBo.size(); i++) {for (const auto& n : vNeiBo[i]) {Union(i, n);}}}int GetConnectRegionIndex(int iNode){int& iConnectNO = m_vNodeToRegion[iNode];if (iNode == iConnectNO){return iNode;}return iConnectNO = GetConnectRegionIndex(iConnectNO);}void Union(int iNode1, int iNode2){const int iConnectNO1 = GetConnectRegionIndex(iNode1);const int iConnectNO2 = GetConnectRegionIndex(iNode2);if (iConnectNO1 == iConnectNO2){return;}m_iConnetRegionCount--;if (iConnectNO1 > iConnectNO2){UnionConnect(iConnectNO1, iConnectNO2);}else{UnionConnect(iConnectNO2, iConnectNO1);}}bool IsConnect(int iNode1, int iNode2){return GetConnectRegionIndex(iNode1) == GetConnectRegionIndex(iNode2);}int GetConnetRegionCount()const{return m_iConnetRegionCount;}vector<int> GetNodeCountOfRegion()//各联通区域的节点数量{const int iNodeSize = m_vNodeToRegion.size();vector<int> vRet(iNodeSize);for (int i = 0; i < iNodeSize; i++){vRet[GetConnectRegionIndex(i)]++;}return vRet;}std::unordered_map<int, vector<int>> GetNodeOfRegion(){std::unordered_map<int, vector<int>> ret;const int iNodeSize = m_vNodeToRegion.size();for (int i = 0; i < iNodeSize; i++){ret[GetConnectRegionIndex(i)].emplace_back(i);}return ret;}private:void UnionConnect(int iFrom, int iTo){m_vNodeToRegion[iFrom] = iTo;}vector<int> m_vNodeToRegion;//各点所在联通区域的索引,本联通区域任意一点的索引,为了增加可理解性,用最小索引int m_iConnetRegionCount;};class Solution {public:int Ans(const int N, vector<tuple<int, int, int, int>>& broad, vector<pair<int, int>>& pos) {auto Mask = [&](int r, int c) {return N * r + c; };vector<vector<int>> not1(N * N), neiBo(N * N);for (const auto& [r, c, r1, c1] : broad) {const int m1 = Mask(r - 1, c - 1);const int m2 = Mask(r1 - 1, c1 - 1);not1[m1].emplace_back(m2);not1[m2].emplace_back(m1);}auto Add = [&](int m1, int r, int c) {const int m2 = Mask(r, c);for (const auto& i : not1[m1]) {if (m2 == i) { return; }}neiBo[m1].emplace_back(m2);neiBo[m2].emplace_back(m1);};for (int r = 0; r < N; r++) {for (int c = 0; c < N; c++) {const int m1 = Mask(r, c);if (r + 1 < N) { Add(m1, r + 1, c); }if (c + 1 < N) { Add(m1, r, c + 1); }}}CUnionFind uf(neiBo);vector<int> cnt(N * N);for (const auto& [r, c] : pos) {cnt[uf.GetConnectRegionIndex(Mask(r - 1, c - 1))]++;}int ans = 0;for (int i = 0; i < N * N; i++) {ans += cnt[i] * (pos.size() - cnt[i]);}return ans / 2;}};int main() {#ifdef _DEBUGfreopen(\"a.in\", \"r\", stdin);#endif // DEBUGCInBuff<> ib;int N,K,R;ib >> N >> K >>R ;auto broad = ib.Read<tuple<int,int,int,int>>(R);auto pos = ib.Read<pair<int, int>>(K);#ifdef _DEBUGprintf(\"N=%d,\", N);Out(broad, \",broad=\");Out(pos, \",pos=\");/*Out(edge, \"edge=\");Out(que, \"que=\");*/#endif // DEBUGauto res = Solution().Ans(N,broad,pos);cout << res;return 0;}

单元测试

int N;vector<tuple<int, int, int, int>> broad;vector<pair<int, int>> pos;TEST_METHOD(TestMethod1){N = 3, broad = { {2,2,2,3},{3,3,3,2},{3,3,2,3} }, pos = { {3,3},{2,2},{2,3} };auto res = Solution().Ans(N, broad, pos);AssertEx(2, 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++**实现。