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

    UVA_10534

        这个题目需要用到nlogn求最长上升子序列的算法。

        算法的思想大概是这个样子,维护一个栈,在扫描过程中,如果当前元素比栈顶元素大,那么就将其加入到栈顶,否则在栈内用二分查找找到恰好不小于当前元素的某一值,并将其替换掉。当前元素为终点的最长上升子序列的长度就是该元素入栈时的位置。

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

        简单证明一下其正确性。我们用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>
    #include<string.h>
    #define MAXD 10010
    int N, a[MAXD], f1[MAXD], f2[MAXD], s[MAXD];
    void init()
    {
    int i;
    for(i = 0; i < N; i ++)
    scanf("%d", &a[i]);
    }
    int Min(int x, int y)
    {
    return x < y ? x : y;
    }
    void solve()
    {
    int i, j, k, top, min, mid, max;
    s[top = 0] = -1;
    for(i = 0; i < N; i ++)
    {
    if(a[i] > s[top])
    {
    s[++ top] = a[i];
    f1[i] = top;
    }
    else
    {
    max = top;
    min = 0;
    for(;;)
    {
    mid = (max + min + 1) / 2;
    if(mid == max)
    break;
    if(s[mid] < a[i])
    min = mid;
    else
    max = mid;
    }
    s[mid] = a[i];
    f1[i] = mid;
    }
    }
    s[top = 0] = -1;
    for(i = N - 1; i >= 0; i --)
    {
    if(a[i] > s[top])
    {
    s[++ top] = a[i];
    f2[i] = top;
    }
    else
    {
    max = top;
    min = 0;
    for(;;)
    {
    mid = (max + min + 1) / 2;
    if(mid == max)
    break;
    if(s[mid] < a[i])
    min = mid;
    else
    max = mid;
    }
    s[mid] = a[i];
    f2[i] = mid;
    }
    }
    max = 0;
    for(i = 0; i < N; i ++)
    {
    k = 2 * Min(f1[i], f2[i]) - 1;
    if(k > max)
    max = k;
    }
    printf("%d\n", max);
    }
    int main()
    {
    while(scanf("%d", &N) == 1)
    {
    init();
    solve();
    }
    return 0;
    }


  • 相关阅读:
    Sliverlight之 矢量绘图
    Silverlight之 xaml布局
    七天学会ASP.NET MVC(七)——创建单页应用
    MVC视图之间调用方法总结
    C#取得程序的根目录以及判断文件是否存在
    七天学会ASP.NET MVC (六)——线程问题、异常处理、自定义URL
    [C#] .NET4.0中使用4.5中的 async/await 功能实现异步
    C#中StreamReader读取中文文本出现乱码的解决方法
    七天学会ASP.NET MVC (五)——Layout页面使用和用户角色管理
    常用正则表达式
  • 原文地址:https://www.cnblogs.com/staginner/p/2249127.html
Copyright © 2011-2022 走看看