2023年CSP-S密码锁(洛谷P9752):集合运算与候选筛选策略

一、问题背景
2023年CSP-S竞赛的密码锁问题要求通过观察多个异常状态,推断出原始正确密码的可能组合。密码锁由5个拨圈组成,每次操作可以是单拨圈转动或相邻双拨圈同步转动。
二、核心算法解析
1. 候选密码生成
set<vector<int>> generate_candidates(const vector<int>& state) {
set<vector<int>> candidates;
// 单拨圈操作:每个拨圈独立转动-9到9格(除0)
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; // +20处理负数取模
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;
}2. 集合交集处理
// 取所有观察状态的候选密码交集 set_intersection( common_candidates.begin(), common_candidates.end(), current_candidates.begin(), current_candidates.end(), inserter(temp, temp.begin()) );
三、完整代码实现
#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);
int n;
cin >> n;
vector<vector<int>> states(n, vector<int>(5));
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);
}
cout << common_candidates.size() << endl;
return 0;
}原创内容 转载请注明出处
