最长上升子序列模板(HDU 1950 Bridging signals)

这篇讲的挺好的:https://blog.csdn.net/lxt_Lucia/article/details/81206439

最长上升子序列 O(n^2)dp模板

最长上升子序列的话,我们用dp [ i ] 表示截至a [ i ] 为止(以a[ i ]结尾)的最长上升子序列的长度,这也是为什么我们初始化dp[ i ]为1,因为开始就是已经包含了a[ i ]的。
如果在a [ i ]之前有比a [ i ]小的数a [ j ] ,就说明a [ i ]可以加到a [ j ]处的最长子序列后,因为可能 j 从1-> i-1 有多个小于a[ i ]的数a[ j ],所以取其中长度的较大者,即dp [ i ] = max(dp [ i ] ,dp [ j ] +1)

可能很多人都有一个疑问,就是这样处理dp,并不一定是最长上升子序列,对的,就是这样的,但是这并不影响我们要的答案,我们只是需要最长的长度,dp的更新虽然使最长子序列里的值发生了变化,但是并不影响长度。

代码:

const int maxN=4e4+5;
const int maxC=1e6+5;

int T,n,dp[maxN],a[maxN],ans;

void init()
{
    fill(dp,dp+n+1,1);
    ans=0;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        init();
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<i;j++)
            {
                if(a[i]>a[j])
                {
                    dp[i]=max(dp[i],dp[j]+1);
                }
            }
        }
        for(int i=1;i<=n;i++)
        {
            ans=max(ans,dp[i]);
        }
        printf("%d\n",ans);
    }
    return 0;
}

最长上升子序列 O(n*logn) 贪心+二分 模板

HDU 1950 Bridging signals//模板题
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1950

题意:这个题意很简单,就是最长上升子序列的模板。

但是它的题目吧,超级难读,我也是醉了,严重怀疑自己是不是学过英语。n多不认识的词和短语,一大段一大段的不懂,不明白,然后各种有道翻译,终于读到最后的时候,我的天呐,题意竟然就这么简单,写的那么复杂,什么不交叉,架桥之类的QAQ,我哭了。

题解
这里我们用low[ i ]来表示 i 长度的上升子序列目前为止最小的末尾值。如果我们能保证末尾值一直是最小的,就可以得到尽可能长的上升子序列。
我们跑一个for循环,如果a[ i ] > low[ ans ] (ans是当前最长子序列的长度,也是low数组的长度),那么我们可以直接将a[ i ]加到low[++ans]数组中;否则,我们就找到low数组中第一个大于等于a[ i ]的数,并用a[ i ]替换掉它,这里的查找就可以直接用lower_bound二分查找(注意是得到的地址-首地址才是下标值)。

这里的话我们low数组中并不是最长公共子序列,也是有了些元素的替换,但是不改变长度,我们做这样的替换是为了后面能够得到更长的序列,可以自行体会一下。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define P(x) x>0?x:0
#define INF 0x3f3f3f3f

using namespace std;
typedef long long ll;
typedef vector<int>:: iterator VITer;
const int maxN=4e4+5;
const int maxC=1e6+5;

int T,n,a[maxN];
int low[maxN],ans;
void init()
{
    fill(low,low+n+1,0);
    ans=0;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        init();
        for(int i=1;i<=n;i++)
        {
            if(a[i]>low[ans])
                low[++ans]=a[i];
            else
                low[lower_bound(low,low+ans+1,a[i])-low]=a[i];//lower_bound返回的是low中第一个大于等于a[i]的地址,再减去首地址就是该元素的下标,替换成a[i]
        }
        printf("%d\n",ans);
    }
    return 0;
}

最长上升子序列(单调不降)
Power OJ 2870

思路:我们要保证可以存在重复的数字,所以说我们的 low 数组就可以存在重复的数字,并且当我们遇到较小的数字时我们要放在 low 数组中第一个大于它的数字的位置上。(必须是不能替代小于等于的数字,否则答案就会偏小)

样例:(用 lower_bound就会错)

6
1 2 5 2 2 2

wrong answer: 3
:)就是直接2每次都覆盖2,所以长度不会变
#include 
#include 
#include 
#include 
using namespace std;
const int maxN=1e5+5;
int ans, a[maxN], low[maxN] , n;

void init()
{
    ans = 0;
    memset(low, 0 , sizeof(low));
}

int LIS()
{
    for(int i = 1; i <= n ; i++)
    {
        if( a[i] >= low[ans])
            low[++ans] = a[i];
        else
            low[ upper_bound(low, low + ans + 1, a[i]) - low ] = a[i];
    }
    return ans;
}

int main()
{
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 1; i <= n ; i++)
            scanf("%d", &a[i]);
        printf("%d\n", LIS());
    }
    return 0;
}

Day Summary:(以下内容可以忽略,日常发牢骚)
今天基本上就只看了这个,最长上升子序列和最长公共子序列,别人前天就已经会了的东西。上午来的时候,难受,不敲题,不看题,发呆,难受,难过,给我妈打电话,她不懂,挂了,回来,十点多吧,又狠着心看了最长公共子序列。到中午吃饭了,然后就又开始发呆,十二点没过多少就吃完了,然后就一直到一点钟,然后就睡了,本来想着一点半起来,然后一直睡到两点钟,又很难受,又发呆,中间还出去骑车骑了一圈,还是不想看题,就……emmm难受呀QAQ,心里难受,非常难受。一直到四点多,终于可以看得下去点儿东西了,就把这个看了,敲了两道题。还是看了人家的博客。我这个人比较情绪化吧,心情不好,心里难受了是完全什么都做不了的,就这个心吧,越难受它就越难受。感觉要病了,抑郁症,自杀离我不远了(当然是开玩笑,我还没活够)但是真的很难受也很难过。至于为什么,我太菜了吧,当然还有别的。本来不想回家的这个暑假,我感觉我快了,回家,遭不住了就直接溜了,不管吧。像我这样的,在哪里都是一样的,做不成事情。
我这几天一直在想,我每天浪费那么多时间难受,发呆,我还在这干啥,还不如回家去耍,但是我又狠不下心走,可能还在可接受范围内吧,这种程度,还没到高三那时候的那样呢,不过也快了我觉得,不说了,为自己祈福,希望可以慢慢好起来。

你可能感兴趣的:(动态规划,#,LIS+LCS+LCIS,最长上升子序列)