zoukankan      html  css  js  c++  java
  • HDU 1257 最少拦截系统(LIS)

    思路:

    1.该题题意是求不严格递减的子序列(即序列中相邻元素可以相等)个数最少有多少个;
    2.有一个结论:该序列中最长递增子序列(LIS)的长度即为不严格递减子序列最少的个数;
    严格证明(有一部分不会证orz):
    (1)首先证明LIS中任意两个元素都来自不同的递减序列:
    如果存在ab,在原序列中,a的位置在b之前,如果它们在LIS中,则满足a<b,如果在同一递减序列中则满足a>=b,矛盾,所以得证;
    (2)再证明n个递减序列中,每个序列抽出一个数,则它们可以成为一条LIS;(这个不太会证orz)
    换一个思路也许可以理解这个结论
    我们从开始往后遍历原序列,同时维护一个最长递增子序列LIS,初始就是第一个数,每往后遍历一个数,在LIS里寻找一个最小的大于等于该数的数,将这个数加到其后面,同时更新该数为当前遍历到的数,如果找不到,则将其加入LIS,那最终LIS长度就是(没有找到的次数+1),自然就是递减序列最少的个数了~;
    例如100 90 80 90 100 70 60 81 82 83
    遍历完前三个数后,LIS里只有一个80,遍历到第四个数90时,在LIS里寻找一个最小的大于等于90的数,可以让90加到后面,发现没有,则就将90加入LIS,100也是这个道理,此时LIS里是[80,90,100],遍历完整个序列后,LIS里就是[60,81,82,83],这个序列出现了3次不能加到前者后面的情况(即需要重新开一个递减序列)==(需要加入值到LIS里),那递减序列最少的个数就等于LIS序列的长度了~
    3.下面的任务就是求LIS的长度了~
    定义序列数组为a[i]
    有两种思路
    (1)定义dp[i]为以a[i]为结尾的LIS长度,得递推关系
    dp[i]=max{1,a[j]+1j<ia[j]<a[i]}dp[i]=max{1,a[j]+1|j<i且a[j]<a[i]}
    该方法复杂度为O(n2)O(n^2)
    (2)定义dp[i]为长度为i的LIS中末尾元素的最小值,想法就是同一长度的子序列,末尾元素更小的序列更具有优势。首先更新所有dp[i]=INF,递推关系就是,从前往后遍历原序列,对于每一个j,如果i==0a[j]>dp[i-1],则
    dp[i]=min(dp[i],a[j])dp[i]=min(dp[i],a[j])
    最终找到的最大的i使得dp[i]!=INF(i+1)即为LIS长度;
    该方法复杂度仅有O(nlogn)O(nlog n)

    代码:

    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int MAX_N=1e4;
    const int INF=1e9;
    int n;
    int a[MAX_N];
    int dp[MAX_N];    //长度为i+1的递增子序列末尾元素的最小值
    void solve(){
    	fill(dp,dp+n,INF);
    	for(int i=0;i<n;i++){
    		*lower_bound(dp,dp+n,a[i])=a[i];
    	}
    	printf("%d
    ",lower_bound(dp,dp+n,INF)-dp);
    } 
    void clear(){
    	for(int i=0;i<=n;i++) a[i]=dp[i]=0;
    }
    int main(){
    	while(~scanf("%d",&n)){
    		for(int i=0;i<n;i++) scanf("%d",a+i);
    		solve();
    		clear();
    	}
    	return 0;
    }
    
  • 相关阅读:
    ssh
    步进器&分栏控制器
    一些过期的整理前端代码
    进度条和滑动条
    UISwitcher
    定时器
    创建window
    iOS开发---UIButton 1 //创建一个可以显示图片的按钮。
    iOS开发---UILabel
    今日头条前端编程题
  • 原文地址:https://www.cnblogs.com/yuhan-blog/p/12308828.html
Copyright © 2011-2022 走看看