2024 接龙 · 方案一 按定义广搜
状态是上一轮的人与末尾元素
正文
// CSP-J 2024 复赛 T4 · 接龙
// 原题:https://oj.yecheng.tv/p/ccf-CSPJ2024D
// 题意:n 个人各有自己的词库 S_i。一轮接龙由某个人 p 挑出自己词库的一个
// 连续子序列 A,长度在 [2, k] 之间;第一轮 A 要以 1 开头,之后每轮 A 要以上一轮
// A 的最后一个元素开头;相邻两轮不能是同一个人。
// 每个任务问:能不能恰好做 r 轮,且最后一轮的 A 最后一个元素正好是 c。
//
// 方案一 · 按定义做 BFS(直观,只能过小数据)
// 状态记成 (上一轮是谁, 上一轮的最后一个元素)。
// 第一轮的起点必须是元素 1;之后从每个状态出发,枚举另一个人 p'、
// 枚举他词库里值等于「上一轮末尾」的位置、再枚举接龙长度 2..k,得到新状态。
// 用 set 存每一轮的状态集合,顺便把这一轮能到达的末尾元素记下来,用来回答任务。
// 复杂度大约是 O(r * n * 词库总长 * k),只能过 n <= 1e3、r <= 10 那批测试点。
#include <bits/stdc++.h>
using namespace std;
vector<int> S[1005];
int main() {
freopen("chain.in", "r", stdin);
freopen("chain.out", "w", stdout);
int T;
cin >> T;
while (T--) {
int n, k, q;
cin >> n >> k >> q;
for (int i = 1; i <= n; i++) {
S[i].clear();
S[i].push_back(0); // 占位,让下标从 1 开始
int l;
cin >> l;
for (int j = 0; j < l; j++) {
int x;
cin >> x;
S[i].push_back(x);
}
}
vector<pair<int, int>> ask(q);
int maxR = 0;
for (int i = 0; i < q; i++) {
cin >> ask[i].first >> ask[i].second;
maxR = max(maxR, ask[i].first);
}
set<pair<int, int>> cur; // (上一轮的人, 上一轮的末尾元素)
for (int p = 1; p <= n; p++) {
for (int j = 1; j < (int)S[p].size(); j++) {
if (S[p][j] != 1) continue; // 第一轮必须以 1 开头
for (int t = 2; t <= k && j + t - 1 < (int)S[p].size(); t++) {
cur.insert(make_pair(p, S[p][j + t - 1]));
}
}
}
vector<set<int>> reach(maxR + 1);
for (set<pair<int, int>>::iterator it = cur.begin(); it != cur.end(); ++it) {
reach[1].insert(it->second);
}
for (int r = 2; r <= maxR; r++) {
set<pair<int, int>> nxt;
for (set<pair<int, int>>::iterator it = cur.begin(); it != cur.end(); ++it) {
int lastPerson = it->first;
int lastValue = it->second;
for (int p = 1; p <= n; p++) {
if (p == lastPerson) continue; // 不能连着同一个人
for (int j = 1; j < (int)S[p].size(); j++) {
if (S[p][j] != lastValue) continue; // 这一轮要接着它开头
for (int t = 2; t <= k && j + t - 1 < (int)S[p].size(); t++) {
nxt.insert(make_pair(p, S[p][j + t - 1]));
}
}
}
}
cur = nxt;
for (set<pair<int, int>>::iterator it = cur.begin(); it != cur.end(); ++it) {
reach[r].insert(it->second);
}
}
for (int i = 0; i < q; i++) {
int r = ask[i].first;
int c = ask[i].second;
cout << (r <= maxR && reach[r].count(c) ? 1 : 0) << "\n";
}
}
return 0;
}
CSP-J 标程 · 复赛真题的其它内容
- 2019 数字游戏 · 方案一 逐字符统计
- 2019 数字游戏 · 方案二 二进制位运算
- 2020 优秀的拆分 · 方案一 贪心从大到小减
- 2020 优秀的拆分 · 方案二 直接看二进制位
- 2021 分糖果 · 方案一 枚举每个 k
- 2021 分糖果 · 方案二 看余数在哪一段
- 2022 乘方 · 方案一 边乘边判断
- 2022 乘方 · 方案二 快速幂加封顶
- 2023 小苹果 · 方案一 照规则真模拟
- 2023 小苹果 · 方案二 只盯住两个数字
- 2024 扑克牌 · 方案一 用集合去重
- 2024 扑克牌 · 方案二 二维布尔表
- 2025 拼数 · 方案一 收集后降序排序
- 2025 拼数 · 方案二 桶计数
- 2019 公交换乘 · 方案一 暴力匹配
- 2019 公交换乘 · 方案二 时间窗口优化
- 2020 直播获奖 · 方案一 每轮排序
- 2020 直播获奖 · 方案二 桶计数
- 2021 插入排序 · 方案一 每次真排一遍
- 2021 插入排序 · 方案二 增量维护有序表
- 2022 解密 · 方案一 二分求整数平方根
- 2022 解密 · 方案二 先估后校开方
- 2023 公路 · 方案一 朴素贪心
- 2023 公路 · 方案二 单调栈预处理
- 2024 地图探险 · 方案一 四方向分支写
- 2024 地图探险 · 方案二 方向数组
- 2025 座位 · 方案一 把座位表填出来
- 2025 座位 · 方案二 直接算排名
- 2019 纪念品 · 方案一 逐天完全背包
- 2019 纪念品 · 方案二 砍掉不赚钱物品
- 2020 表达式 · 方案一 每次重算后缀式
- 2020 表达式 · 方案二 建树加关键性传播
- 2021 网络连接 · 方案一 手写解析
- 2021 网络连接 · 方案二 读入后回拼校验
- 2022 逻辑表达式 · 方案一 递归分治
- 2022 逻辑表达式 · 方案二 递归下降
- 2023 一元二次方程 · 方案一 照公式逐步写
- 2023 一元二次方程 · 方案二 拆成小函数
- 2024 小木棍 · 方案一 先定位数再贪心
- 2024 小木棍 · 方案二 预处理最小数字表
- 2025 异或和 · 方案一 贪心能接就接
- 2025 异或和 · 方案二 动态规划加值域数组
- 2019 加工零件 · 方案一 递归加记忆化
- 2019 加工零件 · 方案二 奇偶最短路
- 2020 方格取数 · 方案一 深搜回溯
- 2020 方格取数 · 方案二 按列动态规划
- 2021 小熊的果篮 · 方案一 每轮扫一遍
- 2021 小熊的果篮 · 方案二 链表加有序集合
- 2022 上升点列 · 方案一 先只考虑不加点
- 2022 上升点列 · 方案二 二维动态规划
- 2023 旅游巴士 · 方案一 分层图加优先队列
- 2023 旅游巴士 · 方案二 状态压成一维
- 2024 接龙 · 方案二 滑动窗口逐轮推进
- 2025 多边形 · 方案一 枚举所有子集
- 2025 多边形 · 方案二 排序加计数 DP