zoukankan      html  css  js  c++  java
  • 不支持【单点删除】下的尺取法

    可以随意转载,但请附带原文地址http://www.cnblogs.com/qswg/p/8620602.html

    问题的提出 

      有些情况下,操作是没有逆的操作,比如max函数。这导致我们没法通过删除端点来移动左指针。

    所以直接用普通尺取来求区间最值是不可能的。

    那么是否有办法是否在不支持删除区间端点的情况下,依旧进行尺取呢。

    答案是有的,而且实现方法很简单,只要用栈做一个缓存就行了。

    算法的实现

      为了方便讨论,我们假设尺取时每次查询区间[li,ri],应满足li<=li+1ri<=ri +1

    算法描述如下

    首先我们声明一下所需的变量:

    p1:左指针

    p2:右指针

    temp :[p1+1,p2】的区间值

    st[]:缓存(栈),其中st[i]代表 [p1-i+1,p1]的区间值

    len:被缓存区间的长度。

    具体流程:

    step1:判断要查询区间[li,ri]是否与缓存区间有交集。若没有执行step2,有则执行step3.

    step2: 构造缓存区:将p1,p1赋值为ri.并更新temp的值,然后依次将区间[ri,ri],[ri-1,ri],[ri-2,ri],……,[li,ri] 压入st[]这个栈内。 返回[li,ri]的值

    step3:(1)对st执行退栈操作,直至栈顶区间的左端点等于li (2)移动右指针至ri,并更新temp(3)返回temp+st.top();

    算法的原理很简单,精髓在于用退栈来实现代替左指针的移动,这样就不用进行删除操作了。

    算法复杂度分析:首先双指针的移动的复杂度肯定是O(n)的,而我们每次缓存一个区间的复杂度是O(区间长度),而这些缓存区间的是不相交的。所以缓存区间的长度和最多是n。

    综上总复杂度依旧是O(n)

    实战

    现在我们用新的算法来解决区间最值的尺取问题

    http://poj.org/problem?id=2823

    Sliding Window
    Time Limit: 12000MS   Memory Limit: 65536K
    Total Submissions: 66282   Accepted: 18836
    Case Time Limit: 5000MS

    Description

    An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example:
    The array is [1 3 -1 -3 5 3 6 7], and k is 3.

    Window positionMinimum valueMaximum value
    [1  3  -1] -3  5  3  6  7  -1 3
     1 [3  -1  -3] 5  3  6  7  -3 3
     1  3 [-1  -3  5] 3  6  7  -3 5
     1  3  -1 [-3  5  3] 6  7  -3 5
     1  3  -1  -3 [5  3  6] 7  3 6
     1  3  -1  -3  5 [3  6  7] 3 7

    Your task is to determine the maximum and minimum values in the sliding window at each position.

    AC代码如下(注意:因为数据量很大,千万不要调用任何的cout和print不然输出会慢10倍以上,进而导致运行时间*10-_-||

     

      1 #include <stdio.h>
      2 #include<stack>
      3 #define MAXN 2000005
      4 #define  MAX 0
      5 using namespace std;
      6 int a[MAXN];
      7 struct Buff
      8 {
      9     int st[MAXN],temp,len,p1,p2;
     10     int type;
     11     void init()
     12     {
     13 
     14         temp=0;
     15         p1=0;
     16         p2=0;
     17         len=0;
     18     }
     19     int fun(int x,int y)
     20     {
     21         if(type==MAX)
     22             return max(x,y);
     23         return min(x,y);
     24     }
     25     int  query(int l,int r)
     26     {
     27         if(l>p1)
     28         {
     29             build(l,r);
     30             return st[len];
     31         }
     32         else
     33         {
     34             while(l+len-1>p1)
     35             {
     36                 len--;
     37             }
     38             while(p2<r)
     39             {
     40                 p2++;
     41                 temp=fun(a[p2],temp);
     42             }
     43             return fun(st[len],temp);
     44         }
     45     }
     46     void build(int l,int r)
     47     {
     48         int i;
     49         if(type==MAX)
     50             st[0]=-2e9-7;
     51         else
     52             st[0]=2e9+7;
     53         len=0;
     54         for(i=r; i>=l; i--)
     55         {
     56             ++len;
     57             st[len]=fun(st[len-1],a[i]);
     58         }
     59         p1=r;
     60         temp=st[0];
     61         p2=r;
     62     }
     63 } q;
     64 inline void Read(int &x)
     65 {
     66     char c=getchar();
     67     int f=1;
     68     x=0;
     69     while (c<'0'||c>'9')
     70     {
     71         if (c=='-')
     72             f=-f;
     73         c=getchar();
     74     }
     75     while (c>='0'&&c<='9')
     76     {
     77         x=x*10+c-'0';
     78         c=getchar();
     79     }
     80     x*=f;
     81 }
     82 inline void Write(int a)
     83 {
     84     if(a<0)
     85     {
     86         putchar('-');
     87         a=-a;
     88     }
     89     if(a>9)
     90         Write(a/10);
     91     putchar(a%10+'0');
     92 }
     93 int main()
     94 {
     95     int n,k,i,x;
     96     scanf("%d%d",&n,&k);
     97     for(i=1; i<=n; i++)
     98     {
     99         Read(a[i]);
    100     }
    101     q.init();
    102     q.type=1;
    103     for(i=1; i<=n-k+1; i++)
    104     {
    105         if(i>1)
    106             putchar(' ');
    107         x=q.query(i,i+k-1);
    108 
    109         Write(x);
    110     }
    111     putchar('
    ');
    112     q.init();
    113     q.type=0;
    114     for(i=1; i<=n-k+1; i++)
    115     {
    116         if(i>1)
    117             putchar(' ');
    118         x=q.query(i,i+k-1);
    119         Write(x);
    120     }
    121     putchar('
    ');
    122     return 0;
    123 }
    带缓存区的尺取

     本题一个比较经典的解法是单调队列,现在我们对比一下两种算法的运行情况。

     

    可以看到单调队列稍快且代码量较少,不过这只是因为我对单调队列没有进行封装而导致的。

    所以无论是从空间,时间,还是编程复杂度上,该算法和单调队列都十分相近的,效率十分优秀。

    总结

    算法的优点:

    本文的算法与单调队列一样是用于处理序列尺取的算法,但是单调队列只能处理最值问题的尺取。而本文的算法几乎能处理任何区间尺取问题(只要单点添加操作是O(1)的)。

    所以更具有通用性

    算法的缺点:

    实现起来比单调队列麻烦得多,而且蕴含的区间信息很少,不像单调队列蕴含着偏序关系,所以可能没法实现某些查询;

    所以最值的尺取问题还是上单调队列比较好。

     习题

    http://www.fjutacm.com/Problem.jsp?pid=3260

    (因为OJ的网页数据较大,第一次打开要花较长时间)

  • 相关阅读:
    【LeetCode】297. 二叉树的序列化与反序列化
    【剑指Offer】面试题37. 序列化二叉树
    【剑指Offer】面试题59
    【剑指Offer】面试题57
    趣图:向客户介绍的产品VS实际开发的产品
    Spring AOP的实现机制
    Mysql高性能优化规范建议
    JavaScript八张思维导图
    深入 Nginx:我们是如何为性能和规模做设计的
    教你用认知和人性来做最棒的程序员
  • 原文地址:https://www.cnblogs.com/qswg/p/8620602.html
Copyright © 2011-2022 走看看