• 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;
    }




  • 相关阅读:
    静态(static)、虚拟(virtual)、动态(dynamic)或消息处理(message)
    SQLLITE
    SQLite数据表和视图
    SQLite
    DELPHI 泛型
    indy10 学习2
    indy10 线程池
    indy
    Indy10 控件的使用(2)TidTCpServer组件学习
    Socket心跳包机制
  • 原文地址:https://www.cnblogs.com/yuzhaoxin/p/2427740.html
Copyright © 2020-2023  润新知