zoukankan      html  css  js  c++  java
  • UVA 10534 Wavio Sequence

    这道题的意思是让我们求一个上升子序列  + 一个下降字序列,且两边的长度是相等的,由于用正常的 O(n2) 算法会 TLE ,所以这里我们采用二分法求最长上升子序列,这里需要利用两个栈来储存“相当于”最长上升子序列的串(我利用一个栈通过再次初始化栈顶top来第二次使用),在此同时开两个数组f1[i],f2[i],分别表示前i个元素的最长字串长度,最后比较同一个位置时(注意f1,f2一个正一个反)两个数组值的大小,取小的一个(因为要求两边长度相等)赋值给新开的数组flag[](我用num不断刷新),最后遍历数组,找出其最大的就可以了。

    这里说一下二分法求最长上升子序列:复杂度为O(n×logn):

    它是通过一个栈来实现的,我们遍历一个母串,如果当前值大于栈顶元素的值,我们将其压入栈,而当前位置 i 的最长上升子序列的长度就是栈顶指针的值(或+1),如果当前值等于栈顶元素的值,不压入栈,同样当前位置 i 的最长上升子序列的值就是栈顶指针的值(或+1),如果当前值小于栈顶元素的值,不压入栈,但是我们要用二分法找出恰好不小于当前值的那个位置,这个位置我们这里定义为x,并将x位置的值替换为当前值,而当前位置 i 的最长上升子序列的长度就是x这个指针的值(或+1);

    为什么这样是成立的呢:别人的证明:

    实际上就是运用了贪心的思想,每次进行替换操作后增强了后续最长上升子序列的“长度增长的潜力”,后续得到的解一定不会比不替换更优。

        简单证明一下其正确性。我们用a[i]替换掉了S[mid],对后续某一元素a[j]来讲,可能会认为此时a[j]前的元素标号并不是严格升序了,这会不会有问题呢?实际是不会有问题的。我们不妨设a[j]左边的元素依次为A1、A2、……,这样A1我们必然可以不动,因为此前A1已经更新至最新了,标号必然是所有曾在这个位置的元素中的标号最大的一个,那么对于A2呢,我们不妨想想现在的A1刚进入这个栈的时候吧,那个时候的A2必然也在那个时候之前更新到当时的最新了吧,并且A1左边的元素的个数从那个时刻起也没有变过吧?现在这个问题就可以不断递归下去了,我们按这种方式一定可以找到一个序号严格递增的序列,即使这个序列并不是在a[j]入栈时我们所看到的序列,但这又有什么关系呢,反正结果对就是了。

        再简单说明一下其最优性。既然前面已经证明了其正确性,那么按前面的方法至少可以得到一个长度可观的最长上升子序列,但究竟其是否是最长的呢?按上面算法的思路,如果想要得到的长度更长的话,那么只有一个措施,就是将原来的替换操作变成插入操作,要不然是没办法增加长度的。然而如果将替换操作变成插入操作,我们还能保证一定可以得到一个标号严格升序的上升子序列吗?显然不能。比如一次插入操作在S[k]和S[k+1]之间,我们插入了一个a[i],但是如果此时有a[j]>S[k+1],那么a[j]的左边是没办法构造出一个长度为k+2的标号严格升序的上升子序列的。既然第一次插入操作都会有错,那肯定就不用考虑后续再有插入操作了。

    代码如下:

    #include<stdio.h>
    #define MAXN 10010
    int a[MAXN], f[MAXN], f1[MAXN], f2[MAXN];
    int n,num;
    int up_bd(int *f, int t, int v)
    {
    int m;
    int first = 0;
    while(first < t)
    {
    m = first + (t - first)/2;
    if(f[m] < v) first = m + 1;
    else t = m;
    }
    f[first] = v;
    return first;
    }
    void solve()
    {
    int top;
    top = 0;
    f[0] = a[0];
    f1[0] = 1;
    int x,y;
    for(int i = 1; i < n; i ++)
    {
    if(a[i] > f[top]) {f[++top] = a[i];x = top + 1;}
    else if(a[i] == f[top]) {f[top] = a[i];x = top + 1;}
    else if(a[i] < f[top]) x = up_bd(f,top,a[i])+1;
    f1[i] = x;
    }
    top = 0;
    f[0] = a[n-1];
    f2[0] = 1;
    for(int i = n-2; i >= 0; i --)
    {
    if(a[i] > f[top]) {f[++top] = a[i];y = top + 1;}
    else if(a[i] == f[top]) {f[top] = a[i];y = top + 1;}
    else if(a[i] < f[top]) y = up_bd(f,top,a[i])+1;
    f2[n-1-i] = y;
    }
    num = 0;
    int min = 0;
    for(int i = 0; i < n; i ++)
    {
    if(f1[i] > f2[n-i-1]) min = f2[n-i-1];
    else min = f1[i];
    if(min > num) num = min;
    }
    printf("%d\n",2*num - 1);
    }
    void input()
    {
    while(scanf("%d",&n) == 1)
    {
    for(int i = 0; i < n; i ++)
    scanf("%d",&a[i]);
    solve();
    }
    }
    int main()
    {
    input();
    return 0;
    }




  • 相关阅读:
    eslint 的 env 配置是干嘛使的?
    cookie httpOnly 打勾
    如何定制 antd 的样式(theme)
    剑指 Offer 66. 构建乘积数组
    剑指 Offer 65. 不用加减乘除做加法
    剑指 Offer 62. 圆圈中最后剩下的数字
    剑指 Offer 61. 扑克牌中的顺子
    剑指 Offer 59
    剑指 Offer 58
    剑指 Offer 58
  • 原文地址:https://www.cnblogs.com/yuzhaoxin/p/2427740.html
Copyright © 2011-2022 走看看