Gym 101653O Diamonds 总结 LCS LIS

2 篇文章 0 订阅
1 篇文章 0 订阅

做到一题,顺便总结一下 LIS LCS
先给出一道 n2 的 DP 做法求LIS
原题链接:
Gym 101653 O Diamonds

大意:
给一个序列,每个点代表一个项链,有两个值 w,c 要求最长上升子序列,只不过现在要同时考虑两个

思路:
题目的范围很宽,直接n2算法能直接过,一开始写nlogn算法不知道为什么会错,好像是因为重载过后 lower_bound仍然不能用,有别于一维的做法.

直接n2 DP做法

DP方程:
f(i)=max{f(j)+1,f(i)}(j<i,aj<ai) f ( i ) = m a x { f ( j ) + 1 , f ( i ) } ( j < i , a j < a i )
具体代码:

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>

using namespace std;
#define INF 0x3f3f3f3f
#define M(a, b) memset(a, b, sizeof(a))
const int MAXN = 2000 + 5;
struct node {
    double w, c;
} a[MAXN];
int dp[MAXN];
int main() {
    int T;
    cin >> T;
    while(T--) {
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> a[i].w >> a[i].c;
            dp[i]=1;
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j < i; j++) {
                if(a[i].w>a[j].w && a[i].c<a[j].c) dp[i]=max(dp[j]+1,dp[i]);
            }
        }
        int ans=0;
        for(int i=1;i<=n;i++) ans=max(ans,dp[i]);
        cout<<ans<<endl;
    }
}
一维的DP做法和这个同理,区别只在于”大小于号的重载”
nlogn做法:

详见:http://blog.csdn.net/shuangde800/article/details/7474903

定义d[k]:长度为k的上升子序列的最末元素,若有多个长度为k的上升子序列,则记录最小的那个最末元素。
注意d中元素是单调递增的,下面要用到这个性质。
首先len = 1,d[1] = a[1],然后对a[i]:若a[i]>d[len],那么len++,d[len] = a[i];
否则,我们要从d[1]到d[len-1]中找到一个j,满足d[j-1]
用lower_bound函数实现

关键代码:

for(int i=0;i<n;i++){
    if(a[i]>=dp[len])
       dp[++len]=a[i];
    else {
        int p=lower_bound(dp,dp+len,a[i])-dp;
        dp[p]=a[i];
    } 
}
printf("%d\n",len);

else 里面更新的意义在于为后面的服务,
可能存在序列{1,3,7,9} 遇到 6 需要改为 {1,3,6,9} 那么后面再遇到 7,8 就能修改为 {1,3,6,7,8}

另外一种 n2 做法 排序+LCS (Longest Commen Sequence) 最长公共子序列

LCS的复杂度为 M*N
方法很好理解.
LCS的做法需要理解三个转移方程:
详见:
http://blog.csdn.net/hrn1216/article/details/51534607

状态转移方程:

这里写图片描述

输出LCS:
倒推回去,从末尾开始输出,选择c大的一个方向逆推.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值