Monotonous queue - Detailed principles (deque realized)

A monotonous queue concept:

Monotone queue, i.e., monotonically increasing or monotonically decreasing queue.

Second, queue monotonous nature:

1. The position of the queue element in the original list by the access (sequential cycle with the team).

2. The size of the queue element is a monotonically increasing or decreasing.

Third, the queue monotonic characteristics:

Into the column from the tail, the tail or the head of the queue column.

Four, example analysis:

So monotonous queue What use is it? Monotone general queue for seeking the most value in the range problem. Look a few questions, understand the context:

1. Luo Gu P1886 sliding window: https://www.luogu.org/problemnew/show/P1886

It did not look hard for two cycles to get, but to know that this algorithm time complexity is o (n ^ 2), according to data given topic will certainly be out. What we want is the time complexity is o (n) algorithm, look at the code:

#include <bits/stdc++.h>

using namespace std;

const int N=1e6+5;

int m[N];           //用于储存最大值序列

struct node{        //队列的节点,包含元素在列表中原来的位置和值
    int order;
    int value;
}tmp;

deque<node>maxn,minn;    //定义节点类型单调队列,分别记录区域内最大值和最小值

int main()
{
    int n,k,t;           //n,k如题目中含义,t用于暂时储存输入
    int m_lo=0;          //最大值序列下标
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&t);

        if(!maxn.empty() && i-maxn.front().order>=k) maxn.pop_front();    //单调队列的原理部分
        if(!minn.empty() && i-minn.front().order>=k) minn.pop_front();    //具体看下方说明
        while(!maxn.empty() && maxn.back().value<t) maxn.pop_back();
        while(!minn.empty() && minn.back().value>t) minn.pop_back();

        tmp.value=t;            //节点入列
        tmp.order=i;
        maxn.push_back(tmp);
        minn.push_back(tmp);   

        if(i>=k-1)    //当达到题目要求区间长度时就开始输出最小值序列,并储存最大值序列
        {
            if(i==n-1) printf("%d\n",minn.front().value);
            else printf("%d ",minn.front().value);
            m[m_lo++]=maxn.front().value;
        }

    }

    for(int i=0;i<m_lo;i++)    //输出最大值序列
    {
        if(i==m_lo-1) printf("%d\n",m[i]);
        else printf("%d ",m[i]);
    }
}

Description: This question is seeking the maximum and minimum values ​​within a range. For a certain element in the array, as long as we care about itself and its k-1 before the number can be.

So how to deal with the queue it? First briefly about the problem-solving ideas, minimum order, for example: a start array subscript i is 0, we constantly put to the queue element, and has maintained the team's first element to a minimum until the first k number, At this point the team first element is the minimum number of pre-k. Then we put the team first output. Continue to go down, go during the subscript out queue (i-k + 1) ~ i kicked number range interval, the first force element to maintain a minimum value of the interval, then the output of the first team element.

Briefly summarized, for each cycle, we have to do is: first kicked beyond the scope of the range of elements, the elements into an array and to ensure that the first team in the current minimum of the interval, the output of the first team, back and forth. Stored within a queue element is placed before the minimum interval monotonically increasing sequence.

So we have to solve two problems:

1. How to remove the element beyond the range of the range: this is the role of the node in the code node, the node stored in the node index of the original array, for each new i-th input, as is the position of the front queue element monotone to-back, as long as the team node.order we first element and i to judge, this time to see if i-node.order> = k can be, let the team is the first element of the column.

2. How to ensure the team's first element is within the range minimum: For a certain interval, we each enter a number, take a look at whether the value of the tail than it large elements, this element is let out of the line until the queue is empty or the tail element values ​​smaller than this number, then we put this number into them, this time the team first element is the minimum range, the output can be.

Reasonable Analysis: From the beginning of the queue is empty, we put the number 0, up to k-1, every time the input of all let the tail current input value is less than the value of the column, it is easy to know at this time first team It is [0, k-1] is the minimum. Then the next first input number k, it is certain that the interval [1, k] and the minimum value of the minimum value [0, k-1] is related. There are two cases, one [. 1, k] from the previous minimum number of k, in this case, we see the [0, k-1] of the case, if the minimum value of [0, k-1] from bit 0, it means that there must queue elements other than element 0, i.e., there must queue [1, k-1] is the minimum sequence, and they are monotonically increasing. At this time, since the first range beyond the range of 0, the column, does not affect the interval [1, k] to find the minimum value. If [0, k-1] from the minimum value [1, k-1], it does not affect the natural [1, k] to find the minimum value. Second, the minimum value is k, then the operation to empty the queue, then it can be put, at this time the team is the first k, that is the minimum. In the case of general, also has the above-mentioned relationship.

To point to note:

1. Write if and while the time be sure to write whether the queue is empty, or if and while the operation will overflow.

2. determine whether the first team squad and queue length does not determine the relationship, because the queue does not necessarily contain all the elements of the entire range.

3. For repeating elements to be removed are not removed can ensure that at least one queue is not removed without affecting the first team, and in the face of smaller values ​​are out of the team.

4. This algorithm is for only one cycle, and each access element only once, the time complexity is o (n). 

 

2. P1440 for the minimum interval within the m: https://www.luogu.org/problemnew/show/P1440

This question is the subject may say that is not very clear, see Example: The first two are the minimum number 4 in the first two minimum number 3 is also 1.

This question and one question on essentially the same, except that this time window does not include the current element, that element is not currently involved in the comparison, and each time the result of a cycle of the output can be.

#include <bits/stdc++.h>

using namespace std;
const int N=2e6+5;
int a[N];
struct node{
    int order;
    int value;
}tmp;
deque<node>vis;
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        tmp.order=i;
        tmp.value=a[i];
        if(i==0) printf("%d\n",0);
        else printf("%d\n",vis.front().value);
        if(i-vis.front().order>=m) vis.pop_front();
        while(!vis.empty() && vis.back().value>a[i]) vis.pop_back();
        vis.push_back(tmp);
    }
}

3. P2032 Scan: https://www.luogu.org/problemnew/show/P2032

Template questions, write directly to:

#include <bits/stdc++.h>

using namespace std;
struct node{
    int order;
    int value;
}tmp;
deque<node>vis;
int main()
{
    int n,k,t;
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&t);
        if(!vis.empty() && i-vis.front().order>=k) vis.pop_front();
        while(!vis.empty() && vis.back().value<t) vis.pop_back();
        tmp.order=i;
        tmp.value=t;
        vis.push_back(tmp);
        if(i>=k-1)
        {
            printf("%d\n",vis.front().value);
        }
    }
}

4. P1714 cut the cake: https://www.luogu.org/problemnew/show/P1714

This problem may at first glance seem to get done with a ruler, but in fact not, because after there is no way to move the pointer around fallback.

So this question how monotonous queue it? First of all to problem into question the current position of all the elements and pre-minus x elements and maximum values. To obtain the maximum value, only the first x and minimum elements. So we have replaced every array from 0 to the current position and using the minimum required monotone queue element before the current element, and then subtracting the minimum value with the current value of the element, each may be compared.

#include <bits/stdc++.h>

using namespace std;

const int N=1e6+5;
int a[N],sum[N];
struct node{
    int order;
    int value;
}tmp;
deque<node>vis;
int main()
{
    int n,m;
    int ans;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        if(i==0) sum[0]=a[i];
        else sum[i]=sum[i-1]+a[i];
        if(!vis.empty() && i-vis.front().order>m) vis.pop_front();     //注意为 > ,自行理解
        while(!vis.empty() && vis.back().value>sum[i]) vis.pop_back();
        tmp.value=sum[i];
        tmp.order=i;
        vis.push_back(tmp);
        if(i==0) ans=sum[i]-vis.front().value;
        else if(i<m) ans=max(ans,sum[i]);
        else ans=max(ans,sum[i]-vis.front().value);
    }
    printf("%d\n",ans);
}

5. P1725 Novo Lu Qi: https://www.luogu.org/problemnew/show/P1725

It is estimated there are problems when a bug in question, but not control, because the data is given by way of a bug.

This is a question dp First, the state transition equation of dp [i] = max (dp [x]) + a [i]; wherein the (ir) <= x <= (il);

Since the required interval dp [ir] ~ dp maximum value of [IL] in, so monotonous queue used.

Cycle starts directly from l, l position as before come to the stage, DP values ​​are 0.

Monotone queues to maintain is the maximum value of dp (note not of a), and to be maintained (this is why the definition j) is the lower limit of the interval difference L i, the interval of fixed length r-l + 1.

#include <bits/stdc++.h>

using namespace std;

const int N=1e6+5;

int a[N],dp[N];

struct node{
    int order;
    int value;
}tmp;

deque<node>vis;

int main()
{
    int n,l,r;
    scanf("%d%d%d",&n,&l,&r);
    for(int i=0;i<=n;i++) scanf("%d",&a[i]);
    for(int i=0;i<l;i++) a[i]=0;        //从0到l-1是走不到的,所以把值改为0
    for(int i=n+1;i<=n+r;i++) a[i]=0;
    int j=0;     //j是要维护的元素下标
    for(int i=l;i<=n+r;i++)
    {
        if(!vis.empty() && j-vis.front().order>=r-l+1) vis.pop_front();
        while(!vis.empty() && vis.back().value<dp[j]) vis.pop_back();
        tmp.order=j;
        tmp.value=dp[j];
        vis.push_back(tmp);
        dp[i]=vis.front().value+a[i];
        j++;
    }
    int ans=-9999999;
    for(int i=n+1;i<=n+r;i++)
    {
        ans=max(ans,dp[i]);
    }
    cout<<ans<<endl;
}

6. P2629 good news, bad news: https://www.luogu.org/problemnew/show/P2629

This same problem and to use elements, the idea is again copied onto a end of the array, and then sliding window of size n, to find the minimum value in the window, and subtracting the value in front of the window, to see if large equal to zero.

#include <bits/stdc++.h>

using namespace std;

const int N=2e6+5;

int a[N],sum[N];

struct node{
    int order;
    int value;
}tmp;

deque<node>vis;

int main()
{
    int n,k,ans;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
        a[i+n]=a[i];
        if(i==0) sum[i]=a[i];
        else sum[i]=sum[i-1]+a[i];
    }
    for(int i=n;i<2*n;i++) sum[i]=sum[i-1]+a[i];
    k=0;        //k用于记录窗口前的位置下标
    ans=0;
    for(int i=0;i<2*n-1;i++)
    {
        if(!vis.empty() && i-vis.front().order>=n) vis.pop_front();
        while(!vis.empty() && vis.back().value>sum[i]) vis.pop_back();
        tmp.order=i;
        tmp.value=sum[i];
        vis.push_back(tmp);
        if(i==n-1 && vis.front().value>=0) ans++;
        else if(i>n-1 && vis.front().value-sum[k]>=0) ans++;
        if(i>n-1) k++;
    }
    cout<<ans<<endl;
}

7. P2422 good feeling: https://www.luogu.org/problemnew/show/P2422

Case of solving the problem is to find the idea array can contain a maximum of how each section in a case where a [i] for the minimum value (since all values ​​are greater than 0, so the minimum value a [i] is to make the interval [i, j] the largest extent possible, so that the a [i] is multiplied by the element and on the maximum interval), and then comparing each of a [i] and the elements within the corresponding section of the product can, difficulty that strike zone range.

In this question, the monotonic function is the queue of a configuration of a monotonically increasing sequence. For each element of the team, if it is less than the last element of the queue, described range limit trailing elements has been determined, i.e., the end of the element itself, and the range ceiling tail element is a number of the front queue tail elements, sum value and the lower limit subtracting the upper limit value of the sum, the largest element interval is obtained in the case where a [i] for the minimum value and can contain. Other special circumstances reference code.

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

const int N=3e6+5;

LL a[N],sum[N],multi[N];

struct node{
    LL order;
    LL  value;
}tmp;
deque<node>vis;

int main()
{
    LL n,lo,bef,aft,ans;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%lld",&a[i]);
        if(i==0) sum[i]=a[i];
        else sum[i]=sum[i-1]+a[i];
    }
    a[n]=0;        //这个很重要,否则到最后一个数的时候队列没办法清空
    for(int i=0;i<=n;i++)    //注意是<=,原因和上面一样
    {
        while(!vis.empty() && vis.back().value>a[i])
        {
            lo=vis.back().order;
            aft=i-1;
            vis.pop_back();
            if(!vis.empty()) bef=vis.back().order;
            else bef=-1;
            if(bef==-1) multi[lo]=sum[aft];
            else multi[lo]=sum[aft]-sum[bef];
        }
        tmp.order=i;
        tmp.value=a[i];
        vis.push_back(tmp);
    }
    ans=-1;
    for(int i=0;i<n;i++)
    {
        ans=max(ans,multi[i]*a[i]);
    }
    cout<<ans<<endl;
}

 

Published 34 original articles · won praise 26 · views 10000 +

Guess you like

Origin blog.csdn.net/sinat_40471574/article/details/90577147
Recommended