题目描述:N(1<=N<=50005)个学生站成一个纵队,每个人只能看到前面身高比他高(严格大于)的人
求所有人中能看到的最大人数
分析:对于某个人A,设前面第一个身高比他高的人是B。如果B不存在,那么A看到0个人。若B存在,那么A既能看到B,也能看到B所能看到的人(PS:题意是每个人只能看到身高严格大于自己的人,身高与自己相同的人视为看不见)。
状态表示:F(x)表示x能看到的人数
转移方程:F(A)=F(B)+1
怎么才能很快找到A前面第一个比他高的人是谁?对于每个人x,记录他前面第一个比他高的人legt[x]。对于每个人A,我们比较A和A-1的高矮,如果A-1比A高,那么A-1自然是第一个比他高的人。否则我们寻找第一个比A-1高的人,也就是left[A-1],因为在A-1与left[A-1]之间的所有人,身高都比A-1矮,他们也自然不可能比A高。比较A与left[A-1]的高矮,如果不行再找left[left[A-1]]...,直到找到比A高的人,或者发现没有一个人比A高。然后可以用dp求解了。
计算left的代码如下,h为每个人的高度:
for(int i=0;i<N;i++){
int j;
for(j=i-1;j>=0&&h[j]<=h[i];j=left[j]) ;
left[i]=j;
}
1 #include<cstdio> 2 int T,n,h[50005],num[50005],left[50005]; 3 int main() 4 { 5 scanf("%d",&T); 6 while(T--) 7 { 8 scanf("%d",&n); 9 for(int i=0; i<n; i++) 10 scanf("%d",&h[i]); 11 int best=0; 12 for(int i=0; i<n; i++) 13 { 14 int j; 15 for(j=i-1; j>=0&&h[j]<=h[i]; j=left[j]) 16 ; 17 left[i]=j; 18 if(j==-1) num[i]=0; 19 else num[i]=num[j]+1; 20 best= best>num[i] ? best : num[i]; 21 } 22 printf("%d ",best); 23 } 24 return 0; 25 }