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(n\log 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\n",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;
}
posted @ 2019-11-23 17:25  YuhanのBlog  阅读(117)  评论(0编辑  收藏  举报