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

【哈希映射实现的并集查找】P5962 [BalticOI 2004] ships 船|普及+

本文涉及知识点

C++并集查找

P5962 [BalticOI 2004] ships 船

题目描述

有一个由 n×nn\times nn×n 的正方形组成的“船”的游戏棋盘。每个单元格可能属于某艘船(黑色)或为空。如果两个边相邻的单元格都是黑色,那么这两个单元格属于同一艘船。不同船之间没有公共边。船的吨位是这些相邻的单元格数。

下边样例,棋盘中(黑色)的单元格属于船,共有一艘 292929 吨的船,三艘 777 吨的船,二艘 444 吨的船,三艘 111 吨的船。

样例

请写一个程序,对给定的游戏棋盘计算出每艘船的吨位和数量。

输入格式

第一行包含一个整数 nnn 表示游戏棋盘的大小。

以下 nnn 行,第 i+1i+1i+1 行描述第 iii 行的列(船)的信息,中间用一个逗号分隔:
如果是单独一个数,那么这一列属于船,这列的左、右单元格是空的;
如果是-连接的两个数,表示这两列之间的所有格子(包含这两列)都属于船,则左侧和右侧是空的。
数据之间用逗号分隔,每行结尾的分号。行中没有空格。如果某行只有一个分号,则这行没有船的信息。

输出格式

你的程序必须输出船的信息。每行是一个空格分隔的两个整数。第一个数是船的吨位,第二个数是这个吨位的船只数量。必须以递减顺序输出船的吨位,并且至少有一艘船有此吨位。

输入输出样例 #1

输入 #1

12
2-4,7,9;
1,4,11-12;
1,4,10,12;
1,4-8,10-12;
1,8;
1,3-6,8,10-12;
1,3,5-6,8,11;
1,8,10-12;
1-8;
;
2;
2-4,7-10,12;

输出 #1

29 1
7 3
4 2
1 3

说明/提示

对于所有的数据,1≤n≤3×1041\le n\le 3\times 10^41n3×104,船舶总数和船舶吨位都不超过 10310^3103

并集查找

如果直接用向量实现的并集查找,空间复杂度O(NN)≈O(9×108)O(NN)\approx O(9\times 10^8)O(NN)O(9×108)空间超限。
船数和吨位数数都不过千,故总点数不超过10610^6106, 故用哈希映射实现的并集查找。时间复杂度:O(点数)。
如果第r行c1到c2列都是船,且c1-1和c2+1都不是船。则连接(r,c1)和(r,x)。c1≤x≤c2c1\le x \le c2c1xc2注意:还要向上连接。
解析各行:
遇到分行解析结束。
如果数字解析数字 到nums[cnt]
解析到-cnt++。
解析到逗号,处理此线段,cnt清理0。

代码

核心代码

#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 <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 CUnionFindMap {
public:bool Union(int n1, int n2) {if (!m_mPar.count(n1)) { m_mPar[n1] = n1; }if (!m_mPar.count(n2)) { m_mPar[n2] = n2; }const int g1 = Fresh(n1);const int g2 = Fresh(n2);if (g1 == g2) { return false; }m_mPar[g1] = g2;return true;}bool IsConnect(int iNode1, int iNode2){const int g1 = GetConnectRegionIndex(iNode1);if (-1 == g1) { return false; }return g1 == GetConnectRegionIndex(iNode2);}int GetConnectRegionIndex(int n) {if (!m_mPar.count(n)) { return -1; }return Fresh(n);}unordered_map<int, vector<int>> GetNodeOfRegion() {unordered_map<int, vector<int>> ret;for (const auto& [n, tmp] : m_mPar) {ret[Fresh(n)].emplace_back(n);}return ret;}unordered_map<int, int> m_mPar;
private:int Fresh(const int n) {if (n == m_mPar[n]) { return n; }return m_mPar[n] = Fresh(m_mPar[n]);}
};class Solution {
public:vector<int> Ans(vector<string>& grid) {const int R = grid.size();int num[2] = { 0 };CUnionFindMap uf;unordered_set<int> prec;auto Do = [&](int r, string& s) {int cnt = 0;unordered_set<int> curc;auto DoRange = [&]() {if (0 == cnt) { return; }if (1 == cnt) {num[1] = num[0];}for (int j = num[0]; j <= num[1]; j++) {uf.Union(R * r + num[0] - 1, R * r + j - 1);if (prec.count(j)) {uf.Union(R * (r - 1) + j - 1, R * r + j - 1);}curc.emplace(j);}cnt = 0;};for (int i = 0; i < s.length(); i++) {if (32 == s[i]) {continue;//避免复制过程中产生空格}else if ('-' == s[i]) {}else if (';' == s[i]) {DoRange();break;}else if (',' == s[i]) {DoRange();}else {int tmp = 0;while (isdigit(s[i])) {tmp = tmp * 10 + s[i] - '0';i++;}num[cnt++] = tmp;i--;}}prec.swap(curc);};for (int r = 0; r < R; r++) {Do(r, grid[r]);}vector<int> ans(1001);auto m = uf.GetNodeOfRegion();for (auto& [tmp, v] : m) {sort(v.begin(), v.end());ans[v.size()]++;}return ans;}
};int main() {
#ifdef _DEBUGfreopen("a.in", "r", stdin);
#endif // DEBUGios::sync_with_stdio(0); cin.tie(nullptr); cout.tie(nullptr);	int R;cin >> R ;auto grid = Read<string>(R);
#ifdef _DEBUG//printf("N=%d,M=%d", N,M);Out(grid, ",grid=");//Out(edge, ",edge=");//Out(ope, ",ope=");
#endif // DEBUGauto res = Solution().Ans(grid);for (int i = res.size() - 1; i >= 0; i--) {if (0 == res[i]) { continue; }cout << i << " " << res[i] << "\n";}return 0;
}

单元测试

	vector<string> grid;TEST_METHOD(TestMethod11){grid = { "2-4,7,9;","1,4,11-12;","1,4,10,12;","1,4-8,10-12;","1,8;","1,3-6,8,10-12; ","1,3,5 - 6,8,11; ","1,8,10 - 12; ","1 - 8; ","; ","2; ","2 - 4,7 - 10,12; "};auto res = Solution().Ans(grid);vector<int> act(1001);act[29] = 1;act[7] = 3;act[4] = 2;act[1] = 3;AssertEx(act, 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/284218.html

相关文章:

  • 【析精】Landmark-Guided Subgoal Generation in Hierarchical Reinforcement Learning
  • 【加解密与C】Base系列(六)Base100
  • 基于在线地图的路径规划测评对比-综合对比城区、农村及城乡结合处的导航
  • JavaScript进阶篇——第八章 原型链、深浅拷贝与原型继承全解析
  • 20250717 Ubuntu 挂载远程 Windows 服务器上的硬盘
  • Linux C 进程基本操作
  • 冒泡排序、选择排序、插入排序、快速排序
  • NLP——迁移学习
  • 【unity组件介绍】URP Decal Projector贴花投影器,将特定材质(贴花)投影到场景中的其他对象上。
  • RabbitMQ深度解析:从核心概念到实战应用
  • 【Android】EditText使用和监听
  • 聚观早报 | 英伟达股价再创新高;中国联通eSIM手机业务开通上线;中国AI加速出海 阿里云提供全栈能力支持
  • Linux之Zabbix分布式监控篇(二)
  • Flutter基础(前端教程①②-序列帧动画)
  • element-plus——图标推荐
  • 【JAVA新特性】Java 8 新特性实战
  • 网络安全威胁下的企业困境与破局技术实践
  • string + 栈 bitset 可达性统计(拓扑排序)
  • 浅探C语言的回调函数(Callback Function)
  • CD54.【C++ Dev】vector和list的反向迭代器的实现
  • 大语言模型Gemini Deep Research 全流程解读+使用攻略
  • uniapp云打包安卓
  • ADVB时序图
  • Spring Boot基于AOP的本地/远程调用动态路由实践
  • 如何在银河麒麟桌面系统中启用 sudo 密码的星号反馈
  • QT之openGL使用(一)
  • 燕之屋打造多元化产品组合,引领行业迈向高质量发展新里程
  • 心通达OA知识管理平台:高效解决单位知识管理难题
  • 点晴模切ERP帮忙模切行业向数智化管理转型
  • Rocky Linux 9 源码包安装php7