zoukankan      html  css  js  c++  java
  • 排列LCS问题

    题目描述

    给你两个长度为 (n) 的排列 (P_1)(P_2),这两个排列只包含 ([1,n]) 范围内的整数各一个,求这两个排列的LCS(即:最长公共子序列)的长度。

    输入格式

    输入的第一行包含一个整数 (n(1 le n le 10^5)),用于表示排列的长度。
    输入的第二行包含 (n) 个整数,两两之间以一个空格分隔,用于表示排列 (P_1)
    输入的第三行包含 (n) 个整数,两两之间以一个空格分隔,用于表示排列 (P_2)

    输出格式

    输出一个整数,用于表示 (P_1)(P_2) 的 LCS 长度。

    样例输入

    5
    1 2 3 4 5
    2 5 3 1 4
    

    样例输出

    3
    

    说明/提示

    样例说明:
    样例中,(P_1)(P_2) 的 LCS 为 ([2, 3, 4])

    暴力解法

    暴力解法时间复杂度 (O(n^2)),空间可以优化到 (O(2n)),但是仍然会超时,实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 100010;
    int f[2][maxn], n, a[maxn], b[maxn];
    int main() {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i];
        for (int i = 1; i <= n; i ++) cin >> b[i];
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= n; j ++) {
                if (a[i] == b[j]) f[i%2][j] = f[(i-1)%2][j-1] + 1;
                else f[i%2][j] = max(f[(i-1)%2][j], f[i%2][j-1]);
            }
        }
        cout << f[n%2][n] << endl;
        return 0;
    }
    

    稀疏性质+线段树优化

    由于刚才的分析中,如果 (P_1[i] e P_2[j]),则 (d[i,j]) 不能转移到 (d[i+1,j+1]),因为目标函数不能增加。这样,我们应该只关心满足 (P_1[i] = P_2[j]) 的状态 (d[i,j]),也就是说,可以把状态转移方程写成:

    [d[i,j] = max { d[i',j'] } + 1, ext{ } P_1[i] = P_2[j], ext{ } i' gt i, j' gt j ]

    显然对于任意的 (i),满足 (P_1[i] = P_2[j])(j) 有且仅有一个,令 (pair(i)) 为这个唯一的值,则一共只有 (n) 个需要考虑的状态,即 (d[i, pair(i)]),那么边界条件是 (d[n, pair(n)] = 1)。按照 (i=n-1,n-2, cdots, i) 的顺序来计算,则只要把目前计算出来的所有状态 (d[i', pair(i')])(它们自然满足 i' gt i)用合理的方式组织起来,可以很快地从其中找到满足 (pair(i') gt pair(i)) 的最大状态即可。

    基于上述思路,使用线段树来存储 (pair(i)) 和对应的 (d[i, pair(i)]) 信息。

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 100010;
    int n, a[maxn], b[maxn], pos[maxn], ans; // pos[b[i]] = i,因为pair在C++中有特殊用法,所以这里用pos[i]来表示pair(i)
    
    int f[maxn<<2];
    void push_up(int rt) {
        f[rt] = max(f[rt<<1], f[rt<<1|1]);
    }
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    void update(int p, int v, int l, int r, int rt) {
        if (l == r) f[rt] = v;
        else {
            int mid = (l + r)/2;
            if (p <= mid) update(p, v, lson);
            else update(p, v, rson);
            push_up(rt);
        }
    }
    int query(int L, int R, int l, int r, int rt) {
        if (L <= l && r <= R) return f[rt];
        int mid = (l + r) / 2, res = 0;
        if (L <= mid) res = max(res, query(L, R, lson));
        if (R > mid) res = max(res, query(L, R, rson));
        return res;
    }
    
    int main() {
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i];
        for (int i = 1; i <= n; i ++) {
            cin >> b[i];
            pos[b[i]] = i;
        }
        for (int i = 1; i <= n; i ++) {
            int j = pos[a[i]];
            int tmp = 1;
            if (i != 1 && j != 1) tmp = max(tmp, query(1, j-1, 1, n, 1)+1);
            update(j, tmp, 1, n, 1);
            ans = max(ans, tmp);
        }
        cout << ans << endl;
        return 0;
    }
    
    
  • 相关阅读:
    2016年蓝桥杯B组C/C++决赛题解
    2016年蓝桥杯B组C/C++决赛题目
    线段树区间更新 费马小定理|魔豆传奇
    2015年蓝桥杯B组C/C++决赛题解
    欧拉线性筛 与 欧拉函数 + 几道例题
    2015年蓝桥杯B组C/C++决赛题目
    2017年蓝桥杯B组C/C++决赛题解
    2017年蓝桥杯B组C/C++决赛题目
    2019 蓝桥杯国赛 B 组模拟赛 题解
    2018年蓝桥杯B组C/C++决赛题解
  • 原文地址:https://www.cnblogs.com/quanjun/p/13193561.html
Copyright © 2011-2022 走看看