2023年 CSP-S 的“ 密码锁 ”题目( 洛谷P9752 )要求破解一种环形密码锁机制。题目给定n组状态,每个状态由5个数字组成,通过“单拨圈”或“双相邻拨圈”操作可改变数字。正确密码需满足:通过操作能从初始 状态转换 到所有给定状态,且给定状态本身不能作为密码。题目核心在于寻找符合条件的正确密码候选集,并排除无效状态。
1. 动态生成候选密码:
设计generate_candidates函数,针对每个状态分别进行单拨圈(单个数字±[1,9])和双相邻拨圈(相邻两数字同时±[1,9])操作,生成所有可能的合法候选密码集合。
利用set容器自动去重,避免重复候选。
2. 集合交集 筛选:
初始化第一个状态的候选集,依次与其他状态候选集进行交集运算(set_intersection)。
若交集为空,说明无解;否则持续缩小共同候选集范围。
3. 排除观察状态:
题目明确指出给定状态本身不是正确密码,因此需从最终候选集中移除所有原输入状态。
1. 输入处理:读取n组状态,存储为二维vector。
2. 生成初始候选:调用generate_candidates计算第一个状态的候选集。
3. 逐步交集筛选:
循环遍历其余n-1组状态,每组生成候选后与当前共同候选集求交集。
若交集为空,退出循环(无解)。
4. 排除观察状态:遍历原输入状态,从共同候选集中删除。
5. 验证结果:若剩余候选集非空,输出任意一个元素;否则输出-1。
#include <iostream> #include <vector> #include <set> #include <algorithm> using namespace std; // 生成所有可能的正确密码候选 set<vector<int>> generate_candidates(const vector<int>& state) { set<vector<int>> candidates; // 单拨圈操作 for (int i = 0; i < 5; ++i) { for (int delta = -9; delta <= 9; ++delta) { if (delta == 0) continue; // 跳过不变操作 vector<int> candidate = state; // 复制状态 candidate[i] = (candidate[i] - delta + 20) % 10; // 处理边界(负数转正) candidates.insert(candidate); } } // 双相邻拨圈操作 for (int i = 0; i < 4; ++i) { for (int delta = -9; delta <= 9; ++delta) { if (delta == 0) continue; vector<int> candidate = state; candidate[i] = (candidate[i] - delta + 20) % 10; candidate[i+1] = (candidate[i+1] - delta + 20) % 10; candidates.insert(candidate); } } return candidates; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); // 加速IO int n; cin >> n; vector<vector<int>> states(n, vector<int>(5)); // 存储n组状态 for (int i = 0; i < n; ++i) { for (int j = 0; j < 5; ++j) { cin >> states[i][j]; } } // 第一个状态的所有候选 set<vector<int>> common_candidates = generate_candidates(states[0]); // 与其他状态的候选取交集 for (int i = 1; i < n; ++i) { set<vector<int>> current_candidates = generate_candidates(states[i]); set<vector<int>> temp; set_intersection( common_candidates.begin(), common_candidates.end(), current_candidates.begin(), current_candidates.end(), inserter(temp, temp.begin()) ); common_candidates = temp; // 更新交集结果 if (common_candidates.empty()) break; // 无解退出 } // 排除观察到的状态本身(题目说明这些不是正确密码) for (const auto& state : states) { common_candidates.erase(state); // 从候选集中删除原状态 } // 输出结果 if (common_candidates.empty()) { cout << -1 << endl; } else { // 输出任意一个候选密码(由于集合无序,可能输出不同结果) for (const auto& candidate : common_candidates) { for (int num : candidate) { cout << num << '; } cout << endl; break; // 找到第一个即退出 } } return 0; }
本解法巧妙结合动态生成候选与集合交集运算,高效缩小正确密码范围。关键在于:
1. 利用数学规律简化状态转换(单/双拨圈操作);
2. 通过交集逐步筛选,减少无效候选;
3. 明确排除题目规定的无效状态。
该思路对处理多约束条件的状态搜索问题具有参考价值,代码中set容器与 STL 算法 的结合提升了效率与可读性。
原创内容 转载请注明出处
题目重解:数字三角形是一个经典的动态规划问题,给定一个由数字组成的三角形结构,从顶部出发,每次可以移动到下方相邻的数字,最终到达底部。我们需要找到一条路径,使得路径上经过的数字总和最大。这个问题可以很...
一、题目解读题目要求在一个n×m的网格中,从左上角到右下角选择一条路径,路径上的数字可重复取用,求取数之和的最大值。路径限制为仅能向右或向下移动。需注意路径的灵活性与重复取数的可能性,传统单向动态规划...
一、题目解读2024年蓝桥杯省B组“传送阵”题目要求处理一个包含n个节点的图,节点间存在单向传输关系。每个节点i可传送至a[i]指定的节点,形成可能存在的环结构。题目需求解从任意节点出发能到达的最长路...
一、题目解读LeetCode 120题“三角形最小路径和”要求给定一个由数字组成的三角形,从顶部开始向下移动,每次可向左或向右移动一格,计算从顶至底的最小路径和。三角形以二维向量形式给出,每层元素数量...
一、题目解读洛谷P4551题要求在一个无向图中,寻找任意两点路径权值异或后的最大值。题目输入为图的边信息(点数n和n-1条边),每条边包含起点、终点及权值。需输出所有路径中权值异或的最大值。问题核心在...
一、题目解读CSP-J 2019年的“公交换乘”题目(洛谷P5661)要求模拟地铁与公交交替出行的费用计算。题目核心在于地铁消费会产生优惠券,而公交可在45分钟内使用优惠券抵扣车费。需要处理n条出行记...
192 浏览 牛客
168 浏览 GESP
157 浏览 入门组
153 浏览 GESP
141 浏览 GESP