POJ 2452 Sticks Problem (线段树+递归)
题意:给一个正整数序列,记为a1,a2,……an,现需找i,j,使得i<j,并且ai与aj之间的数均比ai大,均比aj小,求符合上述条件的i与j,使得j-i的最大。
分析:易知序列中的最小值比其他所有数都小,最大值比其他所有数都大,所以可以现找到最小的数与最大的数的位置,记为i,j,若i<j,则区间[i,j]内符合上述条件的最值就是j-i,然后递归求解左右区间[1,i-1],[j+1,n],若i>j,则将区间分为三段递归求解,[1,j],[j+1,i-1],[i,n]。递归边界为区间左端点>=区间右端点时返回-1。每次求区间的最大与最小值的位置可以用线段树解决。
View Code
#include <stdio.h> #define N 50010 int n; int a[N]; int max[4*N],min[4*N]; int MIN(int i,int j) { return a[i]<a[j]?i:j; } int MAX(int i,int j) { return a[j]>a[i]?j:i; } void update(int cur) { int ls=cur<<1,rs=cur<<1|1; min[cur]=MIN(min[ls],min[rs]); max[cur]=MAX(max[ls],max[rs]); } void build(int cur,int x,int y) { int mid=x+y>>1,ls=cur<<1,rs=cur<<1|1; if(x==y) { min[cur]=max[cur]=x; return; } min[cur]=n+1; max[cur]=0; build(ls,x,mid); build(rs,mid+1,y); update(cur); } int query_min(int cur,int x,int y,int s,int t) { int mid=x+y>>1,ls=cur<<1,rs=cur<<1|1; if(x==s && y==t) return min[cur]; if(mid<s) return query_min(rs,mid+1,y,s,t); if(mid+1>t) return query_min(ls,x,mid,s,t); int l=query_min(ls,x,mid,s,mid); int r=query_min(rs,mid+1,y,mid+1,t); return MIN(l,r); } int query_max(int cur,int x,int y,int s,int t) { int mid=x+y>>1,ls=cur<<1,rs=cur<<1|1; if(x==s && y==t) return max[cur]; if(mid<s) return query_max(rs,mid+1,y,s,t); if(mid+1>t) return query_max(ls,x,mid,s,t); int l=query_max(ls,x,mid,s,mid); int r=query_max(rs,mid+1,y,mid+1,t); return MAX(l,r); } int solve(int x,int y) { if(x<1 || y>n || x>=y) return -1; int id_min=query_min(1,1,n,x,y); int id_max=query_max(1,1,n,x,y); if(id_max>id_min) { int i=solve(x,id_min-1); int j=id_max-id_min; int k=solve(id_max+1,y); k=k>=i?k:i; k=k>=j?k:j; return k; } int i=solve(x,id_max); int j=solve(id_max+1,id_min-1); int k=solve(id_min,y); k=k>=i?k:i; k=k>=j?k:j; return k; } int main() { while(~scanf("%d",&n)) { for(int i=1;i<=n;i++) scanf("%d",&a[i]); a[0]=-1; a[n+1]=100001; build(1,1,n); printf("%d\n",solve(1,n)); } return 0; }