LIS和LDS

USACO08FEB

每次可以改变一个数字,要求使给定的数列变成单调递增或递减,求最小操作数
分析,显然,这个有两个任务,
1.变成单调递增所需操作
2.变成单调递减所需操作
比较一下就是结果。

考虑任务一:在分析后不难发现修改LIS上的数字是不划算的。
证明:设数列An,LIS为数列\(Ai\)\(Aj\)\(Ak\)任务目标是生成一个长度为n的LIS。
情况1:修改非LIS上数字
不难想到,无论修改那个数字都有方法使LIS程度增加1,并且此题最多一次操作能使LIS程度增加1,所以修改非LIS上数能得到更优。
情况2:修改LIS上的数字
如果修改LIS上一个数字能使LIS更优,则必定存在一个和这个LIS等长的LIS,将那个视为LIS即回到情况1

任务二和任务一类似,不再赘述。

这样此题便成为求\(n-max(LIS,LDS)\)
再次回到此题数据范围,不难看出动态规划解法的不足,可以用基于二分查找的求法得到\(O(log n)\)解法。

基于二分查找的LIS 解法。
纪录各个长度LIS最后的数字大小。显然,最后一个数字小比最后一个数字大更优,设\(Ax\)为当前查找的项,若\(Ax\)如果比所有的LIS的大小都大,则可以得到更长的LIS。把\(Ax\)成为更长的LIS的最后一个数字。
否则,利用二分查找找到以\(Ax\)结尾的LIS。

注意,这里的二分查找是upper_bound

代码实现如下

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cstdlib>
using namespace std;
const int maxn = 300000;
int A[maxn],f1[maxn],f2[maxn];
int main()
{
	int n;scanf("%d",&n);
	for(int i = 1;i <= n;i++) scanf("%d",&A[i]);
	int cnt = 1;f1[cnt] = A[1];
	for(int i = 2;i <= n;i++){
		if(A[i] >= f1[cnt]){
			f1[++cnt] = A[i];
		}else
			*upper_bound(f1+1,f1+cnt+1,A[i]) = A[i];
	}
	int ans = cnt;
	cnt = 1;f2[cnt] = A[1];
	for(int i = 2;i <= n;i++){
		if(A[i] <= f2[cnt]) f2[++cnt] = A[i];
		else *upper_bound(f2+1,f2+1+cnt,A[i],greater<int>()) = A[i];
	}
	printf("%d\n",n-max(ans,cnt));
	return 0;
} 
posted @ 2017-06-05 12:18  rsqppp  阅读(341)  评论(0)    收藏  举报