基础动态规划问题之LCS和LIS

LCS:
longest common sequence 即 最长公共子序列
例如
1 3 4 5 6
1 4 5 6 2
最长公共子序列就是 1 4 5 6 即两个序列从左到右删除若干元素能得到的最长相同子序列

动态规划思想
从后往前
设 dp(i, j) 表示第一个数字在i位置,第二个数字在j位置
听不懂,没关系,我们模拟这个过程
一开始由于两个序列都是长度为5
因此从dp(5, 5)开始
并令a[1-5] = {1, 3, 4, 5, 6}
b[1-5] = {1, 4, 5, 6, 2}
首先判断 a[5] = b[5] 吗
不等于于是分解成两个子问题
最长公共子序列应该在 max(dp(4, 5), dp(5, 4))
这里我们继续dp(5, 4)
这个时候a[5] == b[4]
于是长度为 1 + dp(4, 3)
这样下去就可以得到最长公共子序列长度是 4
记录路径只需要用一个string保存一下到最后边界 i == 0 || j == 0判断一下即可

代码如下:

#include<bits/stdc++.h>
#define LL long long
#define ms(s) memset(s, 0, sizeof(s))
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define INF 0X7fffffff
using namespace std;
const int maxn = 1e4 + 10;

int a[maxn];
int b[maxn];
int n, m;
int len = 0;
string ans;

void dp(int i, int j, string ret) {
    if(i == 0 || j == 0) {
        if(ret.length() >= ans.length()) {
            ans = ret;
        }
        return;
    } 
    if(a[i] == b[j]) {
        ret += a[i] + '0';
        dp(i - 1, j - 1, ret);
    } else {
        dp(i - 1, j, ret);
        dp(i, j - 1, ret);
    } 
}

int main() {
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    cin >> m;
    for(int i = 1; i <= m; i++) {
        cin >> b[i];
    }
    dp(n, m, "");
    reverse(ans.begin(), ans.end());
    cout << ans << endl;
    return 0;
}

LIS
Longest increasing sequence
最长上升子序列
这里直接介绍nlogn的算法
主要是贪心
比如 1 3 5 7 2 4 8
一开始令 len[1] = 1 ans = 1 这里的ans表示最长的长度
然后遇到3 -> 由于3比 len[++ans] 来的大 于是 len[2] = 3 ans = 2
遇到5 -> 同3 len[3] = 5 ans = 3
遇到7 -> 同5 len[4] = 7 ans = 4
遇到2 -> 找到第一个比2大的放在那个位置上 即len[2] = 2
遇到4 -> 找到第一个比4大的放在哪个位置上 即len[3] = 4
遇到8 -> 同7 len[5] = 8 ans = 5
这样就得到了最长子序列的长度 5
当然啦这个算法并不能求得路径,关于nlogn算法要求路径,笔者暂未遇到这种情况
因此下面也给出了用n^2算法求路径的方法,思想主要是dp

代码:

#include<bits/stdc++.h>
#define LL long long
#define ms(s) memset(s, 0, sizeof(s))
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define INF 0X7fffffff
using namespace std;
const int maxn = 1e4 + 10;
int a[maxn];
int len[maxn];

int main() {
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >> n;
    int ans = 0;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    ans = 1;
    len[1] = a[1];
    for(int i = 2; i <= n; i++) {
        if(a[i] > len[ans]) {
            ans++;
            len[ans] = a[i];
        }
        else {
            int pos = lower_bound(len + 1, len + ans + 1, a[i]) - len;
            len[pos] = a[i];
        }
    }
    cout << ans << endl;
    return 0;
}

带路径的:

#include<bits/stdc++.h>
#define LL long long
#define ms(s) memset(s, 0, sizeof(s))
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define INF 0X7fffffff
using namespace std;
const int maxn = 1e4 + 10;

// O(n^2)

int n;
int dp[maxn];
int a[maxn];
int prevv[maxn];

void print(int index) {
    if(index == 0) return;
    print(prevv[index]);
    cout << a[index] << " ";
}

int main() {
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    dp[1] = 1;
    for(int i = 1; i <= n; i++) {
        for(int j = i + 1; j <= n; j++) {
            if(a[j] > a[i] && dp[j] < dp[i] + 1) {
                dp[j] = dp[i] + 1;
                prevv[j] = i;
            }
        }
    }
    int ans = 0;
    int index = 0;
    for(int i = 1; i <= n; i++) {
        if(dp[i] > ans) {
            ans = dp[i];
            index = i;
        }
    }
    print(index);
    cout << endl;
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值