题解:P15807 [JOI 2013 Final] 搭乘 IOI 火车 / Take the IOI train

· · 题解

题目大意

有两种车厢 IO,两个车库 ST。刚开始车库中有若干车厢,我们可以先废弃掉两个车库中的一些车厢,之后按顺序从两个车库中取出车厢,组成列车。列车必须以 I 开头且以 I 结尾,中间任意两个相邻的车厢不能是同一个字母。求列车的最大长度。

分析

思路

由此可以想到用动态规划解决此题。

状态设计

定义 f(i,j,k) 表示:当前 S 可用的第一个字符是 S_i1\le i\le M),T 可用的第一个字符是 T_j1\le j\le N),接下来需要的车厢类型为 kk=1 表示需要 Ik=0 表示需要 O)时,继续拼接能获得的最大长度(包含即将取的这一节车厢)。

转移方程

k=1(需要 I)时

k=0(需要 O)时

边界处理

提取答案

枚举所有可能的起点(即丢弃后剩余的后缀),对于每个 1\le i\le M1\le j\le N,调用 f(i,j,1),取最大值即为答案。

参考代码

AC 记录


#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXSZ = 2010;

int n, m;
char s[MAXSZ], t[MAXSZ];
int dp[MAXSZ][MAXSZ][2];

int dfs(int i, int j, int k) {
    int ans = 0;
    if (dp[i][j][k] != -1) return dp[i][j][k];
    if (k == 1) {
        if (i <= n && s[i] == 'I') {
            int x = dfs(i + 1, j, 0);
            ans = max(ans, 1 + x);
        }
        if (j <= m && t[j] == 'I') {
            int x = dfs(i, j + 1, 0);
            ans = max(ans, 1 + x);
        }
    } else {
        if (i <= n && s[i] == 'O') {
            int x = dfs(i + 1, j, 1);
            if (x > 0) ans = max(ans, 1 + x);
        }
        if (j <= m && t[j] == 'O') {
            int x = dfs(i, j + 1, 1);
            if (x > 0) ans = max(ans, 1 + x);
        }
    }
    return dp[i][j][k] = ans;
}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n >> m;
    int ans = 0;
    memset(dp, -1, sizeof(dp));
    for (int i = 1; i <= n; i++) cin >> s[i];
    for (int i = 1; i <= m; i++) cin >> t[i];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            ans = max(ans, dfs(i, j, 1));  // 由于第一项必须为 I,所以从需要找 I 的情况开始深搜。
        }
    }
    cout << ans;
    return 0;
}