1 条题解
-
0
「JOI 2013 Final」搭乘 IOI 火车 题解
题目大意
有两个字符串 和 ,分别代表两个车库中的车厢序列。每个字符是
I或O。火车合法条件:
- 火车两端必须是
I - 相邻车厢必须不同(即
I后面必须是O,O后面必须是I)
操作规则:
- 组装前可以丢弃任意数量的车厢(从字符串开头丢弃)
- 组装时,可以从两个车库的剩余部分轮流取车厢
- 目标是构造最长的合法火车
算法思路
关键观察
合法的火车形如:
IOIOI...OI或IOIO...IOI,即交替的I和O,且两端是I。由于两端必须是
I,所以火车长度一定是奇数。动态规划定义
定义状态: 表示已经使用了 的前 个字符和 的前 个字符,且当前最后一个字符的类型是 (0表示'O',1表示'I')时,能组成的最大火车长度。
状态转移方程
对于每个状态 ,有两种选择:
-
从 取下一个字符:
- 如果 匹配当前需要的字符类型 ,则可以接上
- 转移:$dp[i+1][j][v] = \max(dp[i+1][j][v], \max(v, dp[i][j][v \oplus 1] + 1))$
-
从 取下一个字符:
- 如果 匹配当前需要的字符类型 ,则可以接上
- 转移:$dp[i][j+1][v] = \max(dp[i][j+1][v], \max(v, dp[i][j][v \oplus 1] + 1))$
其中 处理了从空序列开始的情况。
初始化与答案
- 所有状态初始化为
- 最终答案是所有 中的最大值(因为火车必须以
I结尾)
代码实现
#include <bits/stdc++.h> using namespace std; const int inf = 1e9; inline char Q(const int &val) { return val & 1 ? 'O' : 'I'; // 0->'O', 1->'I' } int main() { int n, m; scanf("%d %d", &n, &m); string s, t; cin >> s >> t; s = ' ' + s; // 转为1-indexed t = ' ' + t; // dp[i][j][v]: 使用S前i个,T前j个,最后字符类型为v时的最大长度 vector<vector<array<int, 2>>> dp(n + 1, vector<array<int, 2>>(m + 1, {-inf, -inf})); // 动态规划 for (int i = 0; i <= n; i++) { for (int j = 0; j <= m; j++) { if (i != n) { int v = (s[i + 1] == 'O' ? 0 : 1); // 下一个字符的类型 // 两种情况:从空序列开始,或从已有序列接上 dp[i + 1][j][v] = max(dp[i + 1][j][v], max(v, dp[i][j][v ^ 1] + 1)); } if (j != m) { int v = (t[j + 1] == 'O' ? 0 : 1); // 下一个字符的类型 // 两种情况:从空序列开始,或从已有序列接上 dp[i][j + 1][v] = max(dp[i][j + 1][v], max(v, dp[i][j][v ^ 1] + 1)); } } } int ans = 0; // 找所有以'I'结尾的序列的最大长度 for (int i = 0; i <= n; i++) { for (int j = 0; j <= m; j++) { ans = max(ans, dp[i][j][1]); } } printf("%d\n", ans); return 0; }复杂度分析
- 时间复杂度:,其中 和 是两个字符串的长度
- 空间复杂度:
样例验证
样例1:
输入: 5 5 OIOOI OOIOI 输出: 7程序找到的最优解是长度为7的火车
IOIOIOI,符合预期。样例2:
输入: 5 9 IIIII IIIIIIIII 输出: 1只能组成长度为1的火车
I,符合预期。 - 火车两端必须是
- 1
信息
- ID
- 5591
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者