【Jason's_ACM_解题报告】Defense Lines

来源:互联网 发布:程序员招聘要求 编辑:程序博客网 时间:2024/06/06 02:39

Defense Lines

After the last war devastated your country, you - as the king of the land of Ardenia - decided it was high time to improve the defense of your capital city. A part of your fortification is a line of mage towers, starting near the city and continuing to the northern woods. Your advisors determined that the quality of the defense depended only on one factor: the length of a longest contiguous tower sequence of increasing heights. (They gave you a lengthy explanation, but the only thing you understood was that it had something to do with firing energy bolts at enemy forces).


After some hard negotiations, it appeared that building new towers is out of question. Mages of Ardenia have agreed to demolish some of their towers, though. You may demolish arbitrary number of towers, but the mages enforced one condition: these towers have to be consecutive.


For example, if the heights of towers were, respectively, 5, 3, 4, 9, 2, 8, 6, 7, 1, then by demolishing towers of heights 9, 2, and 8, the longest increasing sequence of consecutive towers is 3, 4, 6, 7.


Input 
The input contains several test cases. The first line of the input contains a positive integer Z<=25, denoting the number of test cases. Then Z test cases follow, each conforming to the format described below.
The input instance consists of two lines. The first one contains one positive integer n<=2*10^5 denoting the number of towers. The second line contains n positive integers not larger than 10^9 separated by single spaces being the heights of the towers.


Output 
For each test case, your program has to write an output conforming to the format described below.
You should output one line containing the length of a longest increasing sequence of consecutive towers, achievable by demolishing some consecutive towers or no tower at all.


Sample Input 


5 3 4 9 2 8 6 7 1 

1 2 3 10 4 5 6


Output 

6

这道题非常经典,即使是最快的方法也有两种实现方式,在此我们只讨论思路与最贴近滑动窗口概念的解法(即使用STL实现的方法)。

当然在考虑如何实现之前首先要确定最行之有效的解法,我们考虑到最直观也是最有效的基本解决方法就是枚举两个递增的序列,将两个序列中间的数列删掉就是答案,而这两个递增序列有一个前提条件,就是前一个序列的末尾元素要小于后一个序列的开头元素,否则是无法连接两个序列的。

首先,我们想到的是最暴力的方法,枚举第一个序列的结尾j与第二个序列的开头i,然后计算两个序列满足递增的最大长度是多少。时间复杂度O(N^3)。

其次,我们为了提高效率,可以想到预处理,也就是说我们通过思考发现,连个序列的长度是可以提前算出来的,所以,设G[j]为以j为结尾的最长递增序列的长度;设F[i]为以i为开头的最长递增序列的长度。这样时间复杂度降到了O(N^2)。

最后,我们考虑,是否存在这样一种解法,让我们只枚举i,而j可以通过最快的速度得出。经过探索,我们发现,对于i之前的元素j和jj如果A[j]<A[jj],而G[j]>G[jj],那么就像滑动窗口的最小值问题一样,j元素永远要比jj元素更划算,只要我们选取jj那么就完全可以选择j来是答案更优,所以我们可以舍弃掉jj元素,而以后只考虑j元素。

所以,最后一个方法的问题难点就在于维护这样一个队列,它保存一些二元组(A[i],G[i]),二元组之间有序,按照A[i]来进行排序,而A[i]值唯一(这是由上面的讨论得出的),然后由于上述性质,G[i]也是随之递增的。那么当有新的二元组插入时,我们先找到它的位置,然后与之前的一个元素比对,若新元素的G小于之前一个元素的G则不考虑新元素,舍弃;若不可舍弃,则插入新元素,并与后面的元素进行比对,若后面的元素的G值小于新元素G值,则舍弃后面的元素,知道队列尾部。由于实现的复杂性,我们考虑用STL的集合来实现会方便快捷一些,由于集合的操作复杂度均为O(logN),所以算法的时间复杂度为O(NlogN)。

另外,该算法还可以通过LIS的思想来实现,并避免STL集合的使用。



附代码如下:
#include<cstdio> #include<set>using namespace std;#define MAXN (200000+5)struct CANDIDATE{int a,g;CANDIDATE(int a,int g):a(a),g(g){}bool operator < (const CANDIDATE& rhs) const {return a<rhs.a;}};int n,A[MAXN],G[MAXN],F[MAXN];set< CANDIDATE > s;int main(){int T;scanf("%d",&T);while(T--){scanf("%d",&n);for(int i=0;i<n;i++)scanf("%d",&A[i]);if(n==1){printf("1\n");continue;}//G[i]以i为结尾的最长递增序列的长度 G[0]=1;for(int i=1;i<n;i++){if(A[i-1]<A[i])G[i]=G[i-1]+1; else G[i]=1;}//F[i]以i为开头的最长递增序列的长度 F[n-1]=1;for(int i=n-2;i>=0;i--){if(A[i]<A[i+1])F[i]=F[i+1]+1; else F[i]=1;}s.clear();s.insert(CANDIDATE(A[0],G[0]));int ans=1;for(int i=1;i<n;i++){CANDIDATE tmp(A[i],G[i]);set<CANDIDATE>::iterator it=s.lower_bound(tmp);bool can_be_insert=true;if(it!=s.begin()){CANDIDATE last=*(--it);if(last.g>=tmp.g)can_be_insert=false;int len=last.g+F[i];ans=max(ans,len);}if(can_be_insert){s.erase(tmp);s.insert(tmp);it=s.find(tmp);it++;while( it!=s.end() && tmp.a < it->a && tmp.g >= it->g )s.erase(it++);}}printf("%d\n",ans);}return 0;}

0 0