单调队列总结

来源:互联网 发布:地狱无门 知乎 编辑:程序博客网 时间:2024/05/21 17:29

poj 2823     N个数,每个区间从左到右选取K个数,求每个区间中的最小值,最大值

void  GetMin(){ //保持队列严格单调递增      int i , j  ;      int head = 0 , tail = -1 ;      for(i = 1 ; i <= n ; i++){           while(head <= tail && a[que[tail]] >= a[i]) tail-- ;           que[++tail] = i ;           while(que[tail] - que[head] + 1 > k) head++ ;           if(i == k) printf("%d" , a[que[head]]) ;           else if(i > k) printf(" %d" ,a[que[head]]) ;      }      puts("") ;}void  GetMax(){ //保持队列严格单调递减      int i , j  ;      int head = 0 , tail = -1 ;      for(i = 1 ; i <= n ; i++){           while(head <= tail && a[que[tail]] <= a[i]) tail-- ;           que[++tail] = i ;           while(que[tail] - que[head] +1 > k) head++ ;           if(i == k) printf("%d" , a[que[head]]) ;           else if(i > k) printf(" %d" ,a[que[head]]) ;      }      puts("") ;}



HDU 3415   长度为N的环状序列,求长度<=K的的连续子序列的和,使得和最大

   sum[i] = a[1] + a[2] + .....+ s[i] .
   a[i]   =  sum[i]  - sum[i-1] ;
   a[i] + a[i-1] = sum[i] - sum[i-2] ;
   .......
   a[i] + a[i-1] + .....+ s[i-k+1] = sum[i] - sum[i-k] ; 
如果序列以i 结束,  则和为  sum[i] -  min{ j | sum[j] }   (i-k <= j <=  i-1) 。
void  Ans(){      int i , j  , head = 0 , tail = -1 , ans = -(1<<30) , L , R ;      sum[0] = 0  ;      for(i = 1 ; i <= n ; i++)  sum[i] = sum[i-1] + a[i] ;      for(i = n+1 ; i < n+k ; i++) sum[i] = sum[i-1] + a[i-n] ;      int m = n + k - 1 ;      for(int i = 1 ; i <= m ; i++){           while(head <= tail && sum[que[tail]] >= sum[i-1])  tail-- ; //单调递增           que[++tail] = i-1 ;           while(que[head] < i-k) head++ ;           if(ans < sum[i] -  sum[que[head]]){                 ans = sum[i] - sum[que[head]] ;                 L = que[head] + 1 ;                 R = i ;           }      }      if(L > n) L -= n ;      if(R > n) R -= n ;      printf("%d %d %d\n" , ans , L , R) ;}

HDU 3474  长度为n的项链,珠子为C,J。从某处断开,从断开处往左数或者往右数,到每个点的C个数>=J个数,求断开点的总数


     C = 1 , J = -1 ,维护前缀和。
     1 2 3 4 5 6 7 8 1 2 3  4 5 6 7 8
     [1->8 ]  区间min{sum} >= sum[0] 满足
     [2->8->1]   区间min{sum} >= sum[1] 满足
     [3->8->2]   区间min{sum} >= sum[2] 满足
     .......
  sum 【i-n+1 ,i】最小值 >= sum[i-n] 满足。 
注意这个顺序是顺着数。 dp0[0]  =  [1->8] ,  dp0[1] = [2->8>1] 。
倒着数把串逆序。8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1  dp1[0] = [8->1] ,  dp1[1] = [7->1->8] 。
                 从8 ,1 处断开。 顺【1->8】<=> dp1[0] ,逆【8->1】<=> dp2[8] 。               
 
const int  Max_N = 1000008 ;char str[Max_N] ;int  sum[Max_N*2] , que[Max_N*2] ;int  n  ;void  Solve(bool dp[]){      int i , j  , m = n + n  , head = 0  , tail = -1 ;      sum[0] = 0 ;      for(i = 1 ; i <= n ; i++) sum[i] = sum[i-1] + (str[i] == 'C' ? 1 : -1) ;      for(i = n+1 ; i <= m ; i++) sum[i] = sum[i-1] + (str[i-n] == 'C' ? 1: -1) ;      for(i = 1 ; i < n ; i++){           while(head <= tail && sum[que[tail]] >= sum[i]) tail-- ;           que[++tail] = i ;      }      for(i = n ; i <= m ; i++){           while(head <= tail && sum[que[tail]] >= sum[i]) tail-- ;           que[++tail] = i ;           while(head <= tail && que[head] <= i - n) head++ ;           if(sum[que[head]] >= sum[i-n])              dp[i-n] = 1 ;      }}bool dp[2][Max_N] ;int  main(){     int t  , ans , i , T = 1 ;     cin>>t  ;     while(t--){          scanf("%s" ,str+1) ;          n = strlen(str+1) ;          memset(dp , 0 , sizeof(dp)) ;          Solve(dp[0]) ;          std::reverse(str+1 , str+1+n) ;          Solve(dp[1]) ;          ans = 0 ;          for(i = 0 ; i < n ; i++)  ans += (dp[0][i] | dp[1][n-i]) ;          printf("Case %d: %d\n" ,T++ , ans) ;     }     return  0 ;}

   





0 0
原创粉丝点击