1 条题解

  • 0
    @ 2026-5-5 23:23:42

    注意到,答案是一个形如 a,b,a,b,a, b, a, b, \dots 的序列的长度,其中 aabb 是某些整数。我们固定一个数(比如 aa),然后枚举 bb,并计算当 bb 作为序列的最后一个数时能得到多长的答案。可以注意到,对于固定的 a,ba, b,答案可以用贪心方法计算。我们同样采用这种思路:寻找在固定位置之前、且中间存在数 aa 的最后一个 bb 出现的位置,然后取该位置对应的答案长度再加 22(可以用双指针法查找)。另外还需要考虑 bb 是序列中第一次或第二次出现的情况。

    此外,还有一种使用动态规划的解法。

    两种解法的时间复杂度均为 O(n2)O(n^2)

    如果有人能写出渐进复杂度更优的解法,我将非常高兴。

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 4005;
    
    int b[N];
    int dp[N][N]; // dp[i][j]: 以 b[i] 和 b[j] 结尾的最长长度 (i < j)
    int last[N];  // 临时数组,记录某个值的最近出现位置
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        int n;
        cin >> n;
        for (int i = 1; i <= n; ++i) {
            cin >> b[i];
        }
    
        int ans = 1; // 至少长度为 1
    
        // 初始化 dp 数组
        for (int i = 1; i <= n; ++i) {
            for (int j = i + 1; j <= n; ++j) {
                dp[i][j] = 2; // 至少两个数
            }
        }
    
        // 枚举中间点 i
        for (int i = 1; i <= n; ++i) {
            // 清空 last 数组,注意值域 1e6,需要用 map 或重置技巧
            // 这里用一个数组加时间戳的方法或直接用 map
            // 由于 n=4000,可以用 unordered_map 或 map,但 O(n^2 log) 可能稍慢
            // 更高效:用 vector 记录出现过的值,然后重置
            // 简便起见,用 unordered_map
            unordered_map<int, int> last;
            for (int j = i + 1; j <= n; ++j) {
                // 在 i 之前找值为 b[j] 的位置
                if (last.count(b[j])) {
                    int k = last[b[j]];
                    dp[i][j] = max(dp[i][j], dp[k][i] + 1);
                }
                last[b[i]] = i; // 更新 b[i] 最近出现的位置为 i
                // 注意这个更新要在查询之后,避免自己匹配自己
                ans = max(ans, dp[i][j]);
            }
        }
    
        cout << ans << endl;
    
        return 0;
    }
    
    • 1

    信息

    ID
    6981
    时间
    1000ms
    内存
    256MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者