1 条题解
-
0
思路与算法
从每个观测状态反向枚举45种单圈非零偏移与36种相邻双圈相同非零偏移,用集合求全部状态的候选正确密码交集。
正确性说明
拨圈操作模10可逆,非零偏移的逆仍在1..9,反向枚举恰得到能产生该状态的全部正确密码。对所有观测取交集等价同时能产生全部状态,去重集合计数正是答案。
复杂度
O(81n log81)时间、O(81+n)空间,含状态输入和每次候选集合初始化;n<=8。
易错点
两圈必须相邻且偏移相同;只转一圈不能允许0幅度,五位中的首位0仍是有效数字,不可丢失位数。
参考代码(C++11)
#include <bits/stdc++.h> using namespace std; struct DS { vector<int> p, s; DS(int n) : p(n), s(n, 1) { iota(p.begin(), p.end(), 0); } int find(int u) { while (p[u] != u) { p[u] = p[p[u]]; u = p[u]; } return u; } bool join(int a, int b) { a = find(a); b = find(b); if (a == b) return false; if (s[a] > s[b]) swap(a, b); p[a] = b; s[b] += s[a]; return true; } }; int main() { int n; cin >> n; vector<array<int, 5>> a(n); for (auto &v : a) for (int &x : v) cin >> x; set<int> good; for (int t = 0; t < n; t++) { set<int> cur; for (int i = 0; i < 5; i++) for (int z = 1; z < 10; z++) { auto v = a[t]; v[i] = (v[i] + z) % 10; int code = 0; for (int x : v) code = code * 10 + x; cur.insert(code); if (i < 4) { v[i + 1] = (v[i + 1] + z) % 10; code = 0; for (int x : v) code = code * 10 + x; cur.insert(code); } } if (t == 0) good = cur; else { set<int> out; set_intersection(good.begin(), good.end(), cur.begin(), cur.end(), inserter(out, out.begin())); good.swap(out); } } cout << good.size() << "\n"; }
- 1