以前也遇到过用正常方法O(n^2)超时的的情况,然后就听说有O(nlgn)的方法,查了一下觉得好麻烦就没看。。果然今天又遇见了超时=。= 没办法了 就看了一下这个算法。交了之后3s超时变成了0.172s过~ 没想到差这么大 ~
原来O(n^2)的是用了2重循环,现在这个变成了1个循环+二分,二分时间复杂度是O(lgn),所以成了O(nlgn)。那怎么实现这个算法呢?
方法一:
我们要用一个栈s,s[i]保存当前最长上升子序列长度为i+1并且最后一个元素最小的最后一个元素的位置=。= 这句话我自己都觉得绕。。初始化的时候top=0,s[0]=0,dp[0]=1(dp[i]表示a[i]作为最后一个元素最长上升子序列长度,s[0]=0是因为目前只有a[0],长度是1,最后一个元素编号是0),i从第二个元素循环到最后一个元素,如果a[i]>a[s[top]],则把i入栈,dp[i]=dp[s[top]]+1。如果a[i]<=a[s[top]],就在栈中找一个位置x,使a[s[x]]是大于等于a[i]的最小值,也就是lower_bound,这里就用二分实现。dp[i]就等于dp[s[x]],这时要把s[x]的值换成i,因为a[i]<=a[s[x]],换了之后后面的元素只要比a[i]大而不一定非要比a[s[x]]大就可以使dp等于原来s[x]代表的长度+1,也就是能使序列尽可能长。
举个例子 1 2 6 10 5 6 ,前4个元素入栈后,top=3,此时s中元素分别为 0 1 2 3,发现5比a[s[top]]=10小,调用二分lower_bound,得到位置为2,(a[s[2]]=6,是在s中编号的元素中大于等于5的最小的),所以dp[4]=dp[s[2]]=dp[2],此时用4替代s[2],s变成 0 1 4 3,到第6个元素a[5]=6,小于a[s[top]],同样的方法这时得到的位置是3了,现在a[s[2]]=5了,a[s[3]]=10才是大于等于6的最小值,所以dp[5]=dp[s[3]],再用5替代s[3],s变成 0 1 4 5(表示长度为1的最小可以以a[0]结尾,长度为2的最小可以以a[1]结尾,长度为3的最小可以以a[4]结尾,长度为4的最小可以以a[5]结尾)
方法二:
还是用这个s,而s[i]保存当前最长上升子序列长度为i+1并且最后一个元素最小的最后一个元素的值。思想是一样的,代码稍微有改动,手写这两种方法的lowerbound也不一样,我觉得这个方法比上面的简单,由于s里存的已经是值,就可以直接调用STL里的lower_bound函数,注意lower_bound是前闭后开的区间,返回的是地址,要再减去首地址。
Problem D
Wavio Sequence
Input:StandardInput
Output: Standard Output
Time Limit: 2 Seconds
Wavio is a sequence of integers. It has some interestingproperties.
· Wavio is of odd length i.e. L = 2*n+ 1.
· The first (n+1) integers ofWavio sequence makes a strictly increasing sequence.
· The last (n+1) integers of Waviosequence makes a strictly decreasing sequence.
· No two adjacent integers are same in aWavio sequence.
For example 1, 2, 3, 4, 5, 4, 3, 2, 0 is an Waviosequence of length9. But1, 2, 3, 4, 5, 4, 3, 2, 2 is not avalid wavio sequence. In this problem, you will be given a sequence ofintegers. You have to find out the length of the longest Wavio sequence whichis a subsequence of the given sequence. Consider, the given sequence as :
1 2 3 2 1 2 3 4 32 1 5 4 1 2 3 2 2 1.
Here the longest Wavio sequence is : 1 2 3 4 5 4 3 2 1. So, the outputwill be9.
Input
The input filecontains less than 75 test cases. The description of each test case isgiven below: Input is terminated by end of file.
Each set starts with a postiveinteger, N(1<=N<=10000). In next few lines there will beNintegers.
Output
For each set of input print the length of longest waviosequence in a line.
Sample Input Output for Sample Input
10 1 2 3 4 5 4 3 2 1 10 19 1 2 3 2 1 2 3 4 3 2 1 5 4 1 2 3 2 2 1 5 1 2 3 4 5 | 9 9 1
|
方法一:
#include<cstring>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
int a[10010],s[10010];
int dp1[10010],dp2[10010];
int lowerbound(int x,int y,int v){
while(x<y){
int m=(y+x)/2;
if(a[s[m]]>=v) y=m;
else x=m+1;
}
return x;
}
int main(){
freopen("in.txt","r",stdin);
int N;
while(scanf("%d",&N)!=EOF){
int i,j,top;
for(i=0;i<N;i++){
scanf("%d",&a[i]);
dp1[i]=dp2[i]=1;
}
top=0;
s[0]=0;
for(i=1;i<N;i++) //正向
if(a[i]>a[s[top]]){
s[++top]=i;
dp1[i]=dp1[s[top-1]]+1;
}
else{
int p=lowerbound(0,top+1,a[i]);
dp1[i]=dp1[s[p]];
s[p]=i;
}
top=0;
s[0]=N-1;
for(i=N-2;i>=0;i--) //反向
if(a[i]>a[s[top]]){
s[++top]=i;
dp2[i]=dp2[s[top-1]]+1;
}
else{
int p=lowerbound(0,top+1,a[i]);
dp2[i]=dp2[s[p]];
s[p]=i;
}
int ans=0;
for(i=0;i<N;i++) if(min(dp1[i],dp2[i])*2-1>ans) ans=min(dp1[i],dp2[i])*2-1;
printf("%d\n",ans);
}
return 0;
}
方法二:
#include<cstring>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
int a[10010],s[10010];
int dp1[10010],dp2[10010];
int lowerbound(int x,int y,int v){
while(x<y){
int m=(y+x)/2;
if(s[m]>=v) y=m;
else x=m+1;
}
return x;
}
int main(){
freopen("in.txt","r",stdin);
int N;
while(scanf("%d",&N)!=EOF){
int i,j,top;
for(i=0;i<N;i++){
scanf("%d",&a[i]);
dp1[i]=dp2[i]=1;
}
top=0;
s[0]=a[0];
for(i=1;i<N;i++) //正向
if(a[i]>s[top]){
s[++top]=a[i];
dp1[i]=top+1;
}
else{
int p=lower_bound(s,s+top,a[i])-s;
dp1[i]=p+1;
s[p]=a[i];
}
top=0;
s[0]=a[N-1];
for(i=N-2;i>=0;i--) //反向
if(a[i]>s[top]){
s[++top]=a[i];
dp2[i]=top+1;
}
else{
int p=lower_bound(s,s+top,a[i])-s;
dp2[i]=p+1;
s[p]=a[i];
}
int ans=0;
for(i=0;i<N;i++) if(min(dp1[i],dp2[i])*2-1>ans) ans=min(dp1[i],dp2[i])*2-1;
printf("%d\n",ans);
}
return 0;
}
在网上看的STL里lower_bound的示例,还不怎么会vector,先留着到时再看~
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
using namespace std;
int main()
{
const int VECTOR_SIZE = 8 ;
// Define a template class vector of int
typedef vector<int > IntVector ;
//Define an iterator for template class vector of strings
typedef IntVector::iterator IntVectorIt ;
IntVector Numbers(VECTOR_SIZE) ;
IntVectorIt start, end, it, location ;
// Initialize vector Numbers
Numbers[0] = 4 ;
Numbers[1] = 10;
Numbers[2] = 11 ;
Numbers[3] = 30 ;
Numbers[4] = 69 ;
Numbers[5] = 70 ;
Numbers[6] = 96 ;
Numbers[7] = 100;
start = Numbers.begin() ; // location of first
// element of Numbers
end = Numbers.end() ; // one past the location
// last element of Numbers
// print content of Numbers
cout << "Numbers { " ;
for(it = start; it != end; it++)
cout << *it << " " ;
cout << " }\n" << endl ;
// return the first location at which 10 can be inserted
// in Numbers
location = lower_bound(start, end, 1) ;
cout << "First location element 10 can be inserted in Numbers is: "
<< location - start<< endl ;
}