题目:codevs 3289 花匠
链接:http://codevs.cn/problem/3289/
这道题有点像最长上升序列,但这里不是上升,是最长“波浪”子序列。用动态规划可以解决,方程类似最长上升子序列:
f[i]=max(f[j]) ( 1≤j≤i-1 && ( (f[j]%2=1 && A[j]<A[i] ) || (j%2=0 && A[j]>A[i]) ) )
p[i]=max(p[i]) ( 1≤j≤i-1 && ( (p[j]%2=1 && A[j]>A[i] ) || (j%2=0 && A[j]<A[i]) ) )
结果:
ans=max(f[n],p[n])
...写出来,很恶心的方程,因为题目中是有两种情况,第一种是 第一个元素 < 第二个元素 开始的波浪序列,另一种是 第一个元素 > 第二个元素 开始的波浪序列。我这里的f[i]是第一种情况算出来的最长波浪序列,p[i]是第二种情况算出来的最长波浪序列,然后最后的答案是两者之间选一个最大的。这样用o(n²)的算法可以达成,但是注意,题目中的数据量是10,000 ,肯定时间要超,所以还是要用优化。
最长上升子序列中可以用线段树优化,那么这里怎么优化呢?
我的笨笨做法是开4个线段树:maxfj[],maxfo[],maxpj[],maxpo[]。
maxfj[]维护f[i]是奇数的最大值,maxfo[]维护f[i]是偶数的最大值。
maxpj[]维护p[i]是奇数的最大值,maxpo[]维护p[i]是偶数的最大值。
因为f[i]中,当f[i]是奇数的时候,区间是1到A[i]-1中的最大值,而当f[i]是偶数的时候,区间是A[i]+1到n的最大值(因为是严格单调,所以要+1或-1)。
当p[i]是奇数的时候,区间是A[i]+1到n的最大值,而当p[i]是偶数的时候,区间是1到A[i]-1中的最大值。
所以不能同时维护,要分开来,因此就是4个线段树。
当然,A[i]的值很大,要做离散化。
大致思路就是这样吧。
附代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 using namespace std; 5 const int maxn=100010; 6 7 int n,maxfj[maxn*4],maxfo[maxn*4],f[maxn],p[maxn],maxpj[maxn*4],maxpo[maxn*4]; 8 9 struct u 10 { 11 int v,r; 12 bool operator <(const u &rhs) const 13 { 14 return v<rhs.v; 15 } 16 }A[maxn]; 17 18 bool cmp(u a,u b) 19 { 20 return a.r<b.r; 21 } 22 23 int w,v; 24 void updatefj(int o,int L,int R) 25 { 26 if(L==R) maxfj[o]=max(maxfj[o],v); 27 else 28 { 29 int M=(L+R)/2; 30 if(w<=M) updatefj(o*2,L,M); else updatefj(o*2+1,M+1,R); 31 maxfj[o]=max(maxfj[o*2],maxfj[o*2+1]); 32 } 33 } 34 35 int y1,y2,ans; 36 void queryfj(int o,int L,int R) 37 { 38 if(y1<=L && R<=y2) ans=max(ans,maxfj[o]); 39 else 40 { 41 int M=(L+R)/2; 42 if(y1<=M) queryfj(o*2,L,M); 43 if(y2>M) queryfj(o*2+1,M+1,R); 44 } 45 } 46 void updatefo(int o,int L,int R) 47 { 48 if(L==R) maxfo[o]=max(maxfo[o],v); 49 else 50 { 51 int M=(L+R)/2; 52 if(w<=M) updatefo(o*2,L,M); else updatefo(o*2+1,M+1,R); 53 maxfo[o]=max(maxfo[o*2],maxfo[o*2+1]); 54 } 55 } 56 57 void queryfo(int o,int L,int R) 58 { 59 if(y1<=L && R<=y2) ans=max(ans,maxfo[o]); 60 else 61 { 62 int M=(L+R)/2; 63 if(y1<=M) queryfo(o*2,L,M); 64 if(y2>M) queryfo(o*2+1,M+1,R); 65 } 66 } 67 void updatepj(int o,int L,int R) 68 { 69 if(L==R) maxpj[o]=max(maxpj[o],v); 70 else 71 { 72 int M=(L+R)/2; 73 if(w<=M) updatepj(o*2,L,M); else updatepj(o*2+1,M+1,R); 74 maxpj[o]=max(maxpj[o*2],maxpj[o*2+1]); 75 } 76 } 77 78 void querypj(int o,int L,int R) 79 { 80 if(y1<=L && R<=y2) ans=max(ans,maxpj[o]); 81 else 82 { 83 int M=(L+R)/2; 84 if(y1<=M) querypj(o*2,L,M); 85 if(y2>M) querypj(o*2+1,M+1,R); 86 } 87 } 88 89 void updatepo(int o,int L,int R) 90 { 91 if(L==R) maxpo[o]=max(maxpo[o],v); 92 else 93 { 94 int M=(L+R)/2; 95 if(w<=M) updatepo(o*2,L,M); else updatepo(o*2+1,M+1,R); 96 maxpo[o]=max(maxpo[o*2],maxpo[o*2+1]); 97 } 98 } 99 100 void querypo(int o,int L,int R) 101 { 102 if(y1<=L && R<=y2) ans=max(ans,maxpo[o]); 103 else 104 { 105 int M=(L+R)/2; 106 if(y1<=M) querypo(o*2,L,M); 107 if(y2>M) querypo(o*2+1,M+1,R); 108 } 109 } 110 111 int main() 112 { 113 cin>>n; 114 for(int i=1;i<=n;i++) cin>>A[i].v,A[i].r=i; 115 //离散化 116 sort(A+1,A+n+1); 117 for(int i=1,nw=1;i<=n;i++) 118 { 119 f[i]=1,p[i]=1;//顺便做的f[],p[]初始化 120 if(A[i+1].v!=A[i].v) nw++,A[i].v=nw-1; 121 else A[i].v=nw; 122 } 123 sort(A+1,A+n+1,cmp); 124 125 w=A[1].v,v=f[1]; 126 updatefj(1,1,n); 127 updatepj(1,1,n); 128 129 for(int i=2;i<=n;i++) 130 { 131 y1=1,y2=A[i].v-1,ans=0; 132 if(y2>=y1) queryfj(1,1,n);//注意,因为有A[i]-1,所以要判断区间的存在 133 y1=A[i].v+1,y2=n; 134 if(y2>=y1) queryfo(1,1,n); 135 f[i]=ans+1; 136 v=f[i],w=A[i].v; 137 if(f[i]%2==1) updatefj(1,1,n); 138 else updatefo(1,1,n); 139 140 y1=1,y2=A[i].v-1,ans=0; 141 if(y2>=y1) querypo(1,1,n); 142 y1=A[i].v+1,y2=n; 143 if(y2>=y1) querypj(1,1,n); 144 p[i]=ans+1; 145 v=p[i],w=A[i].v; 146 if(p[i]%2==1) updatepj(1,1,n); 147 else updatepo(1,1,n); 148 } 149 150 cout<<max(f[n],p[n]); 151 return 0; 152 }