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

UVa 1326 Jurassic Remains

题目描述

西伯利亚的古生物学家发现了一批侏罗纪时期的恐龙骨骼碎片。由于骨骼太大,无法整体运输,他们决定将骨骼拆分成单独的骨头,并在关节处用标签标记(标签为大写字母 A - Z),以便在博物馆重新组装。后来,他们又发现了一些新的骨头,并一并送往博物馆。

问题是:

  • 标签不唯一,多个关节对可能使用相同字母标记
  • 新骨头上的标签不一定需要与其他骨头连接
  • 每块骨头上每个字母最多出现一次

任务:选择最多的骨头,使得在选出的骨头集合中,每个标签要么不出现,要么出现偶数次(这样才能让所有关节都配对连接)。


题目分析

关键条件转化

我们需要选出一个骨头子集,满足:

  • 对于任意标签字母,它在所有选中骨头中出现的总次数为偶数
  • 子集大小最大化

关键观察:如果把每个骨头看作一个集合(元素是标签字母),那么条件等价于:所有选中骨头的对称差(异或)为空集。

数学模型

  • 262626 个字母看作位掩码的 262626 位(A 对应第 000 位,Z 对应第 252525 位)
  • 每块骨头转换为一个整数(掩码),某位为 111 表示包含对应字母
  • 问题转化为:从 NNN 个整数中选出一个子集,使得这些数的异或和000,且子集大小最大

解题思路

直接暴力法(可行但较慢)

枚举所有 2N2^N2N 个子集,检查异或和是否为 000,记录最大子集。
N≤24N \leq 24N24 时,224≈1.6×1072^{24} \approx 1.6 \times 10^72241.6×107,在 OJ\texttt{OJ}OJ 上可能勉强通过。

折半搜索(优化方法)

将骨头分成两半:

  • 前半部分:前 N2\frac{N}{2}2N 个骨头
  • 后半部分:剩余骨头

步骤

  1. 枚举前半部分所有子集,记录每个异或值对应的最大子集大小子集掩码
  2. 枚举后半部分所有子集,对于每个异或值,在前半部分查找相同的异或值
    • 如果找到,则合并子集,总大小 = 前半子集大小 + 后半子集大小
    • 更新最大子集
  3. 特别处理前半或后半自身异或和为 000 的情况

复杂度O(N×2N/2)O(N \times 2^{N/2})O(N×2N/2),对于 N=24N = 24N=24 只需约 24×212≈10524 \times 2^{12} \approx 10^524×212105 次操作,比暴力快约 200020002000 倍。

参考代码

// Jurassic Remains
// UVa ID: 1326
// Verdict: Accepted
// Submission Date: 2025-11-03
// UVa Run Time: 0.010s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <bits/stdc++.h>
using namespace std;int main() {int N;while (cin >> N) {vector<int> bones(N);for (int i = 0; i < N; i++) {string s;cin >> s;int mask = 0;for (char c : s) {mask |= (1 << (c - 'A'));}bones[i] = mask;}int maxSize = 0;int bestMask = 0;// 折半搜索int half = N / 2;// 存储前半部分异或值 -> (size, mask)map<int, pair<int, int>> firstHalf;// 枚举前半部分for (int mask = 0; mask < (1 << half); mask++) {int xorSum = 0;int size = 0;for (int i = 0; i < half; i++) {if (mask & (1 << i)) {xorSum ^= bones[i];size++;}}// 如果当前异或值没出现过,或者出现但size更小,则更新if (firstHalf.find(xorSum) == firstHalf.end() || firstHalf[xorSum].first < size) {firstHalf[xorSum] = make_pair(size, mask);}// 如果前半部分自己就能满足条件if (xorSum == 0 && size > maxSize) {maxSize = size;bestMask = mask;}}// 枚举后半部分int secondHalf = N - half;for (int mask = 0; mask < (1 << secondHalf); mask++) {int xorSum = 0;int size = 0;for (int i = 0; i < secondHalf; i++) {if (mask & (1 << i)) {xorSum ^= bones[half + i];size++;}}// 查找前半部分是否有相同的异或值auto it = firstHalf.find(xorSum);if (it != firstHalf.end()) {int totalSize = it->second.first + size;if (totalSize > maxSize) {maxSize = totalSize;// 合并前后两部分的掩码bestMask = (it->second.second) | (mask << half);}}// 如果后半部分自己就能满足条件if (xorSum == 0 && size > maxSize) {maxSize = size;bestMask = mask << half;}}// 输出结果cout << maxSize << endl;if (maxSize > 0) {bool first = true;for (int i = 0; i < N; i++) {if (bestMask & (1 << i)) {if (!first) cout << " ";cout << (i + 1);first = false;}}cout << endl;} else {cout << endl;}}return 0;
}
http://www.dtcms.com/a/565720.html

相关文章:

  • Readest(电子书阅读器) v0.9.91
  • Flink 优化-数据倾斜
  • 遵义网站网站建设江阴便宜做网站
  • 大模型RLHF:PPO原理与源码解读
  • Mojo变量知识点解读
  • Linux之rsyslog(2)输入输出配置
  • 整体设计 全面梳理复盘 之17 三套表制表的支持和支撑以及编程基础 之2
  • 凯文·凯利《2049:未来10000天的可能》
  • 网站百度建设高端网站设计百家号
  • ctypes.pythonapi.PyThreadState_SetAsyncExc作用详解
  • pyside6常用控件: QPushButton()按钮切换、带图片的按钮
  • Python逻辑运算符
  • MinGW下载、安装和使用教程(附安装包,适合新手)
  • lol做任务领头像网站微商城网站建设平台
  • 百日挑战——单词篇(第十二天)
  • (单调队列、ST 表)洛谷 P2216 HAOI2007 理想的正方形 / P2219 HAOI2007 修筑绿化带
  • Spark RDD 编程从驱动程序到共享变量、Shuffle 与持久化
  • 网站 面包屑网站开发工作流审批流
  • 网站建设广金手指六六十四在线建站系统
  • 排序还有分页
  • electron对于图片/视频无法加载的问题
  • TDengine 字符串函数 CHAR 用户手册
  • 股票信息收集系统设计
  • 深圳网站建设 设计首选公司红色扁平化网站
  • 深度学习PINN!从入门到精通!
  • 电子商务网站建设内容新手开店适合开什么店
  • Java 后端面试干货:四大核心模块高频考点深度解析
  • 交换机路由器基础(三)--常见接口、线缆和器件
  • Qt笔记:QtAdvancedStylesheet使用时,关于url(icon:/primary/checklist_invert.svg)的疑惑
  • 亚远景-ISO/PAS 8800在软件定义汽车(SDV)时代的AI安全治理角色