1 条题解
-
0
题目分析
我们有一个未知的数字序列,只知道它由 1-9 的数字组成,并且有若干条线索。每条线索都是从序列的某个位置向某个方向遍历,遇到新数字就记录下来的结果。
我们需要找到满足所有线索的最短序列。
关键观察
-
线索生成规则:每条线索都是按首次出现顺序记录数字,这暗示了序列中数字的相对位置关系
-
搜索可行性:n ≤ 10,序列长度不会太大,可以用搜索解决
-
状态压缩:只有1-9的数字,可以用位运算记录数字出现情况
解题思路
方法:DFS + 剪枝优化
核心思想:逐个位置尝试填入1-9的数字,同时验证当前部分序列是否满足所有线索的约束。
算法步骤:
-
DFS状态:
- 当前构造的序列
- 当前序列长度
-
验证函数:对于当前序列,检查是否能生成所有线索
- 对每个位置、每个方向,模拟生成线索
- 检查生成的线索是否与输入匹配
-
剪枝策略:
- 长度剪枝:当前长度 ≥ 已知最小解时剪枝
- 可行性剪枝:当前序列已违反某些线索时剪枝
- 数字约束剪枝:利用线索间的数字关系提前排除
复杂度分析:
- 最坏情况:O(9^L),其中L是序列长度
- 实际运行:通过剪枝,搜索空间大大减少
代码实现
#include <iostream> #include <vector> #include <algorithm> using namespace std; vector<vector<int>> clues; int n, ans = 100; // 检查当前序列是否满足所有线索 bool check(const vector<int>& seq) { for (auto& clue : clues) { bool found = false; int m = seq.size(); // 尝试每个起点和方向 for (int start = 0; start < m && !found; start++) { // 向右扫描 vector<int> generated; int mask = 0; for (int i = start; i < m; i++) { if (!(mask & (1 << seq[i]))) { generated.push_back(seq[i]); mask |= (1 << seq[i]); } } if (generated == clue) { found = true; break; } // 向左扫描 generated.clear(); mask = 0; for (int i = start; i >= 0; i--) { if (!(mask & (1 << seq[i]))) { generated.push_back(seq[i]); mask |= (1 << seq[i]); } } if (generated == clue) { found = true; break; } } if (!found) return false; } return true; } void dfs(vector<int>& seq) { // 剪枝:当前长度已经超过已知最优解 if (seq.size() >= ans) return; // 检查当前序列是否满足所有线索 if (check(seq)) { ans = min(ans, (int)seq.size()); return; } // 继续扩展序列 for (int num = 1; num <= 9; num++) { seq.push_back(num); dfs(seq); seq.pop_back(); } } int main() { cin >> n; clues.resize(n); for (int i = 0; i < n; i++) { int x; while (cin >> x && x != 0) { clues[i].push_back(x); } } vector<int> seq; dfs(seq); if (ans == 100) cout << -1 << endl; else cout << ans << endl; return 0; }优化建议
- 更高效的验证:可以预处理线索信息,避免每次都完整验证
- 更强的剪枝:记录每个线索的必须满足的约束条件
- 启发式搜索:优先尝试更可能满足线索的数字
总结
本题是典型的约束满足问题,通过DFS搜索所有可能的序列,结合有效的剪枝策略来找到最优解。关键在于理解线索生成的规则,并设计高效的验证和剪枝方法。
-
- 1
信息
- ID
- 5048
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者