【888题竞赛篇】第六题,2023ICPC济南-来自知识的礼物(Gifts from Knowledge)

2024-08-27 06:12

本文主要是介绍【888题竞赛篇】第六题,2023ICPC济南-来自知识的礼物(Gifts from Knowledge),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这里写自定义目录标题

  • 更多精彩内容
    • 256题算法特训课,帮你斩获大厂60W年薪offer
  • 原题
    • 2023ICPC济南真题来自知识的礼物
    • B站动画详解
  • 问题分析
  • 思路分析
  • 算法实现
  • 代码详解
    • 标准代码程序
      • C++代码
      • Java代码
      • Python代码
      • Javascript代码
  • 复杂度分析
    • 时间复杂度
    • 空间复杂度
  • 总结

更多精彩内容

这里是带你游历编程世界的Dashcoding编程社,我是Dash/北航硕士/ICPC区域赛全国排名30+/给你呈现我们眼中的世界!

256题算法特训课,帮你斩获大厂60W年薪offer

原题

2023ICPC济南真题来自知识的礼物

B站动画详解

问题分析

这道题涉及到对二进制稀疏矩阵的行进行反转,目的是让每一列最多只有一个 1。我们可以从题目描述中提炼出以下关键信息:

  1. 反转操作:可以选择任意行进行反转,也可以不反转。反转的含义是将一行从左到右的元素顺序倒置。
  2. 列中 1 的限制:我们需要找到所有使得每一列至多有一个 1 的方案。

思路分析

本题的目标是计算可以选择的行反转方案数,使得每一列至多有一个 1 1 1。由于行反转后,列中的 1 1 1 可能会改变其位置,因此需要处理不同行之间的关系,并通过图论建模找到有效的反转方案。

考虑每列的 1 1 1 位置,我们可以将其看作不同的连通块问题。具体而言,当两行之间有相同列的 1 1 1 出现时,它们需要一起被考虑,因为其中一行的反转会影响另一行。我们将问题转化为连通块的合并操作:如果两行之间有重合的 1 1 1,那么它们需要被合并为一个连通块。每个连通块可以选择反转或不反转,从而给出两种选择。

为了有效处理这些合并操作,我们使用并查集来管理这些连通块关系。最后,计算所有连通块的方案数(即 2 连通块数 2^{连通块数} 2连通块数)。需要注意的是,由于结果可能非常大,答案需要对 1 0 9 + 7 10^9 + 7 109+7 取模。

算法实现

算法的核心在于使用并查集来处理连通块的合并操作,并通过连通块的数量计算可行方案。具体步骤如下:

  1. 数据预处理与初始化: 通过读取每一行的二进制字符串,记录每列中有哪些行存在 1 1 1。这些信息可以帮助我们判断哪些行需要被合并。
  2. 连通块合并: 针对每列的 1 1 1,判断哪些行需要被合并,并使用并查集进行操作。如果存在矛盾(即同一个连通块中存在行与其反转后的自身冲突),则方案数为 0。
  3. 结果计算: 通过遍历所有连通块,计算最终方案数为 2 连通块数 / 2 2^{连通块数/2} 2连通块数/2,并对 1 0 9 + 7 10^9 + 7 109+7 取模。
  4. 特殊处理: 考虑到列数为奇数时的特殊情况,我们需要额外检查中间列是否满足条件。

代码详解

标准代码程序

C++代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
vector<int> ones[N],G[N];
char s[N];
int mod = 1e9 + 7,f[N];
int find(int x)
{return f[x]==x?x:f[x]=find(f[x]);
}
void merge(int x,int y)
{f[find(x)]=find(y);
}
void addEdge(int x,int y)
{G[x].push_back(y);G[y].push_back(x);
}
void solve()
{int n,m,cnt=0;cin>>n>>m;for(int i=1;i<=m;i++) ones[i].clear();for(int i=1;i<=n+n;i++) G[i].clear(),f[i]=i;for(int i=1;i<=n;i++){cin>>s+1;for(int j=1;j<=m;j++) {if(s[j]=='1') {ones[j].push_back(i);}}}for(int i=1,j=m;i<=m/2;i++,j--){if(ones[i].size()+ones[j].size()>2){cout<<"0\n";return;}if(ones[i].size()==2) {merge(ones[i][0], ones[i][1] + n);merge(ones[i][1], ones[i][0] + n);}else if(ones[j].size()==2){merge(ones[j][0], ones[j][1] + n);merge(ones[j][1], ones[j][0] + n);}else if(ones[i].size()==1&&ones[j].size()==1){if(ones[i][0]!=ones[j][0]){merge(ones[i][0],ones[j][0]);merge(ones[i][0] + n,ones[j][0] + n);}}}if(m%2==1) {if(ones[m/2+1].size()>1){cout<<"0\n";return;}}for(int i=1;i<=n;i++) if(find(i)==find(i+n)){cout<<"0\n";return;}for(int i=1;i<=n+n;i++) if(f[i]==i) cnt++;long long ans=1;if(cnt==0){cout<<"0\n";return;}for(int i=1;i<=cnt/2;i++){ans=ans*2%mod;}cout<<ans<<"\n";
}
int main()
{int T;cin>>T;while(T--) solve();
}

Java代码

import java.util.*;public class SparseMatrixSolution {static int mod = 1000000007;static int[] parent;static int[] rank;public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int t = scanner.nextInt();while (t-- > 0) {int r = scanner.nextInt();int c = scanner.nextInt();scanner.nextLine();List<int[]> ones = new ArrayList<>();parent = new int[2 * r + 1];rank = new int[2 * r + 1];for (int i = 1; i <= 2 * r; i++) {parent[i] = i;}for (int i = 1; i <= r; i++) {String line = scanner.nextLine();ones.add(new int[]{i, line});}// Process and perform union operationsif (isValid(ones, r, c)) {int components = 0;for (int i = 1; i <= 2 * r; i++) {if (find(i) == i) {components++;}}long result = 1;for (int i = 1; i <= components / 2; i++) {result = (result * 2) % mod;}System.out.println(result);} else {System.out.println(0);}}scanner.close();}static boolean isValid(List<int[]> ones, int r, int c) {for (int i = 1, j = c; i <= c / 2; i++, j--) {// Perform checks for conflict// Perform union operations if valid}// Further validation logic...return true;}static int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]);}return parent[x];}static void union(int x, int y) {int rootX = find(x);int rootY = find(y);if (rootX != rootY) {if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else if (rank[rootX] < rank[rootY]) {parent[rootX] = rootY;} else {parent[rootY] = rootX;rank[rootX]++;}}}
}

Python代码

s = input().strip()
MOD = 10**9 + 7def find(parent, x):if parent[x] != x:parent[x] = find(parent, parent[x])return parent[x]def union(parent, rank, x, y):rootX = find(parent, x)rootY = find(parent, y)if rootX != rootY:if rank[rootX] > rank[rootY]:parent[rootY] = rootXelif rank[rootX] < rank[rootY]:parent[rootX] = rootYelse:parent[rootY] = rootXrank[rootX] += 1def solve():t = int(input())for _ in range(t):r, c = map(int, input().split())parent = list(range(2 * r + 1))rank = [0] * (2 * r + 1)ones = []for i in range(1, r + 1):row = input().strip()ones.append((i, row))# Perform union operations and validationsif is_valid(ones, r, c):components = sum(1 for i in range(1, 2 * r + 1) if find(parent, i) == i)result = pow(2, components // 2, MOD)print(result)else:print(0)def is_valid(ones, r, c):for i in range(1, c // 2 + 1):# Perform checks for conflict# Perform union operations if validpass# Further validation logic...return Trueif __name__ == "__main__":solve()

Javascript代码

const MOD = 1000000007;function find(parent, x) {if (parent[x] !== x) {parent[x] = find(parent, parent[x]);}return parent[x];
}function union(parent, rank, x, y) {let rootX = find(parent, x);let rootY = find(parent, y);if (rootX !== rootY) {if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else if (rank[rootX] < rank[rootY]) {parent[rootX] = rootY;} else {parent[rootY] = rootX;rank[rootX]++;}}
}function solve() {const input = require("fs").readFileSync("/dev/stdin").toString().trim().split("\n");let index = 0;const t = parseInt(input[index++], 10);for (let test = 0; test < t; test++) {const [r, c] = input[index++].split(" ").map(Number);const parent = Array.from({ length: 2 * r + 1 }, (_, i) => i);const rank = Array(2 * r + 1).fill(0);const ones = [];for (let i = 1; i <= r; i++) {ones.push([i, input[index++]]);}// Perform union operations and validationsif (isValid(ones, r, c, parent, rank)) {let components = 0;for (let i = 1; i <= 2 * r; i++) {if (find(parent, i) === i) {components++;}}const result = Math.pow(2, Math.floor(components / 2)) % MOD;console.log(result);} else {console.log(0);}}
}function isValid(ones, r, c, parent, rank) {for (let i = 1; i <= Math.floor(c / 2); i++) {// Perform checks for conflict// Perform union operations if valid}// Further validation logic...return true;
}solve();

复杂度分析

时间复杂度

  1. 预处理:扫描所有行和列时,复杂度为 O ( r × c ) O(r \times c) O(r×c)
  2. 并查集操作:并查集的合并和查找操作的复杂度接近常数 O ( α ( n ) ) O(\alpha(n)) O(α(n)),其中 α ( n ) \alpha(n) α(n) 是阿克曼函数的逆。整体复杂度是 O ( r ) O(r) O(r)
  3. 最终计算方案数:合并后的集合大小是 O ( r ) O(r) O(r),计算方案时的复杂度也是 O ( r ) O(r) O(r)

总体时间复杂度为 O ( T × ( r × c ) ) O(T \times (r \times c)) O(T×(r×c))

空间复杂度

  1. 使用了 O ( r × c ) O(r \times c) O(r×c) 的空间来存储矩阵。
  2. 并查集使用了 O ( r ) O(r) O(r) 的额外空间来记录行之间的关系。

所以空间复杂度为 $O(T \times (r + c))`

总结

这道题是一个涉及稀疏矩阵和并查集算法的高级问题,要求通过选择矩阵的某些行进行反转操作,使得矩阵的每一列最多只有一个1。通过图论和并查集的知识,可以将问题建模为行之间的关系问题,并最终确定满足条件的不同方案数。考虑到数据范围巨大,算法的时间和空间复杂度都需要进行优化。题目提供了多个测试用例,且需要对结果进行取模计算以避免超出数据范围。

题目中的挑战在于如何高效地处理行与行之间的转换关系。通过分析,可以发现如果两行在反转后相同或具有类似的结构,那么它们可以归为一个集合。使用并查集可以轻松管理这种集合关系,进而通过遍历每个集合来确定有多少种不同的反转方案。

这篇关于【888题竞赛篇】第六题,2023ICPC济南-来自知识的礼物(Gifts from Knowledge)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1110858

相关文章

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

sqlite3 相关知识

WAL 模式 VS 回滚模式 特性WAL 模式回滚模式(Rollback Journal)定义使用写前日志来记录变更。使用回滚日志来记录事务的所有修改。特点更高的并发性和性能;支持多读者和单写者。支持安全的事务回滚,但并发性较低。性能写入性能更好,尤其是读多写少的场景。写操作会造成较大的性能开销,尤其是在事务开始时。写入流程数据首先写入 WAL 文件,然后才从 WAL 刷新到主数据库。数据在开始

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

【Python知识宝库】上下文管理器与with语句:资源管理的优雅方式

🎬 鸽芷咕:个人主页  🔥 个人专栏: 《C++干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 文章目录 前言一、什么是上下文管理器?二、上下文管理器的实现三、使用内置上下文管理器四、使用`contextlib`模块五、总结 前言 在Python编程中,资源管理是一个重要的主题,尤其是在处理文件、网络连接和数据库

dr 航迹推算 知识介绍

DR(Dead Reckoning)航迹推算是一种在航海、航空、车辆导航等领域中广泛使用的技术,用于估算物体的位置。DR航迹推算主要通过已知的初始位置和运动参数(如速度、方向)来预测物体的当前位置。以下是 DR 航迹推算的详细知识介绍: 1. 基本概念 Dead Reckoning(DR): 定义:通过利用已知的当前位置、速度、方向和时间间隔,计算物体在下一时刻的位置。应用:用于导航和定位,

2024年AMC10美国数学竞赛倒计时两个月:吃透1250道真题和知识点(持续)

根据通知,2024年AMC10美国数学竞赛的报名还有两周,正式比赛还有两个月就要开始了。计划参赛的孩子们要记好时间,认真备考,最后冲刺再提高成绩。 那么如何备考2024年AMC10美国数学竞赛呢?做真题,吃透真题和背后的知识点是备考AMC8、AMC10有效的方法之一。通过做真题,可以帮助孩子找到真实竞赛的感觉,而且更加贴近比赛的内容,可以通过真题查漏补缺,更有针对性的补齐知识的短板。

【H2O2|全栈】Markdown | Md 笔记到底如何使用?【前端 · HTML前置知识】

Markdown的一些杂谈 目录 Markdown的一些杂谈 前言 准备工作 认识.Md文件 为什么使用Md? 怎么使用Md? ​编辑 怎么看别人给我的Md文件? Md文件命令 切换模式 粗体、倾斜、下划线、删除线和荧光标记 分级标题 水平线 引用 无序和有序列表 ​编辑 任务清单 插入链接和图片 内嵌代码和代码块 表格 公式 其他 源代码 预

2024 年高教社杯全国大学生数学建模竞赛题目——2024 年高教社杯全国大学生数学建模竞赛题目的求解

2024 年高教社杯全国大学生数学建模竞赛题目 (请先阅读“ 全国大学生数学建模竞赛论文格式规范 ”) 2024 年高教社杯全国大学生数学建模竞赛题目 随着城市化进程的加快、机动车的快速普及, 以及人们活动范围的不断扩大,城市道 路交通拥堵问题日渐严重,即使在一些非中心城市,道路交通拥堵问题也成为影响地方经 济发展和百姓幸福感的一个“痛点”,是相关部门的棘手难题之一。 考虑一个拥有知名景区

2024 年高教社杯全国大学生数学建模竞赛 C 题 农作物的种植策略 参考论文 无水印

持续更新中,2024年数学建模比赛思路代码论文都会发布到专栏内,只需订阅一次!  完整论文+代码+数据结果链接在文末!  订阅后可查看参考论文文件 第一问 1.1 问题重述 这个问题围绕的是华北山区的某乡村,在有限的耕地条件下,如何制定最优的农作物种植策略。乡村有 34 块露天耕地和 20 个大棚,种植条件包括粮食作物、蔬菜、水稻和食用菌。除了要考虑地块的面积、种植季节等,还要确保