不支持【单点删除】下的尺取法

可以随意转载,但请附带原文地址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的网页数据较大,第一次打开要花较长时间)

原文地址:https://www.cnblogs.com/qswg/p/8620602.html