• 最长上升子序列nlogn算法


    LIS问题是经典的动态规划问题,它的状态转移相信大家都很熟悉:

    f[i] = f[k] + 1  (k < i 且 A[k] < A[i])

    显然这样做复杂度是O(n^2)

    有没有更快的算法呢?

    当然,你会发现你在往前找的过程中实际上就是在查询最大值的过程,如果能应用二分就很有机会降到nlogn

    但是原f[]序列并不满足二分性质呐。。怎么办呢?


    我们要的是往前长度最大的,我们的二分目标就是长度

    不妨开一个长度数组len[i],表示长度为i的上升子序列最后末尾的值的最小值

    对于没算出的len,设为INF,每次算完f[i]后,就对len[f[i]]进行更新

    这样子我们就可以每次logn算出f[i]


    为什么这样是正确的呢?

    应为len[]数组是单调递增的,长度为2的末尾值一定比长度为3的要小,所以我们写的二分一定是最大值的二分


    fill(len,len + maxn,INF);
    	len[0] = 0;
    	int ans = 0;
    	REP(i,n){
    		//cout<<A[i]<<' ';
    		int L = 0,R = ans;
    		while (L < R){
    			int mid = (L + R + 1) >> 1;
    			if (len[mid] < A[i]) L = mid;
    			else R = mid - 1;
    		}
    		f[i] = L + 1;
    		len[f[i]] = min(len[f[i]],A[i]);
    		ans = max(ans,f[i]);
    	}
    	cout<<ans<<endl;




  • 相关阅读:
    如何用vue做计算器功能
    js反弹运动
    $.each的使用
    js文字滚动事件
    根据服务器时间,计算出时间轴的倒计时。
    时间格式转时间戳的几种方法
    匀速运动升级
    js匀速运动
    js图片滚动无缝衔接
    webFrame
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282834.html
Copyright © 2020-2023  润新知