单调队列题目

单调队列:http://baike.baidu.com/view/3771451.htm

foj 1894 志愿者选拔 

http://acm.fzu.edu.cn/problem.php?pid=1894

题意:中文略..

思路:

就是很单纯的单调队列,最最入门的单调队列,而且是很形象的排队问题。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)

#define ll long long
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 1000007
#define N 5007
#define M 200007
using namespace std;

struct node
{
    char name[6];
    int rp,num;//记录人品,记录编号
}q[maxn];

int main()
{
    //freopen("din.txt","r",stdin);
    int t;
    char op[10];
    scanf("%d",&t);
    while (t--)
    {
        scanf("%s",op);
        int front = 0,tail = -1;
        int num = 0,leave = 0;
        while (scanf("%s",op))
        {
            if (op[0] == 'E') break;
            if (op[0] == 'C')
            {
                node tmp;
                scanf("%s%d",tmp.name,&tmp.rp);
                tmp.num = ++num;
                while  (tail >= front && q[tail].rp < tmp.rp) tail--;//维护单调性
                q[++tail] = tmp;
            }
            else if (op[0] == 'Q')
            {
                while (tail >= front && q[front].num <= leave) front++;//找到还没出对的最大值
                if (tail >= front) printf("%d\n",q[front].rp);
                else puts("-1");
            }
            else leave++;
        }
    }
    return 0;
}

 

pku 2823 Sliding Window 

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

 题意:

给定长度为n的序列,求从前往后没k个数里面的最大值最小值。

思路:

单调队列维护区间长度为k的队列并取最值,一个维护最大一个维护最小。注意这里队列里面存储的是坐标队头与当前进队的坐标之间的区间最值,开始一直转不过弯了,我们利用这一点来保持队列的维护长度为k.

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)

#define ll long long
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 1000007
#define N 5007
#define M 200007
using namespace std;

int a[maxn],ans[maxn];
int q[maxn];

void solve(int mk,int n,int k)//mk为0维护最小,1维护最大
{
    int front = 0,tail = - 1;
    int i;
    for (i = 0; i < k; ++i)
    {
        if (!mk)
        {
            while (tail >= front && a[i] < a[q[tail]]) tail--;
            q[++tail] = i;
        }
        else
        {
            while (tail >= front && a[i] > a[q[tail]]) tail--;
            q[++tail] = i;
        }
    }
    int len = 0;
    ans[len++] = a[q[front]];
    for (i = k; i < n; ++i)
    {
        while (tail >= front && i - q[front] >= k) front++;//i是当前进队列的坐标与q[front]的差值即为我们队列维护的长度
        if (!mk)
        {
            while (tail >= front && a[i] < a[q[tail]]) tail--;//进队
            q[++tail] = i;
        }
        else
        {
            while (tail >= front && a[i] > a[q[tail]]) tail--;//进队
            q[++tail] = i;
        }
         ans[len++] = a[q[front]];
    }
    for (i = 0; i < len - 1; ++i) printf("%d ",ans[i]);
    printf("%d\n",ans[i]);

}
int main()
{
    //freopen("din.txt","r",stdin);
    int n,k,i;
    scanf("%d%d",&n,&k);
    for (i = 0; i < n; ++i)
    scanf("%d",&a[i]);

    solve(0,n,k);
    solve(1,n,k);

    return 0;
}

由于本题是维护区间最值问题所以可以用线段树和rmq做,线段树很简单毫无压力的区间求值,而rmq这里还会卡内存,我们处理的方法是由于他求的区间长度总k是一个定值,多以我们的f只要开的一维即可。

线段树代码:

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)

#define ll long long
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define ls (rt<<1)
#define rs (rt<<1|1)
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 1000007
#define N 5007
#define M 200007
using namespace std;

int a[maxn<<2],b[maxn<<2],ans1[maxn],ans2[maxn];

void pushup(int rt)
{
    a[rt] = max(a[ls],a[rs]);
    b[rt] = min(b[ls],b[rs]);
}
void build(int l,int r,int rt)
{
    if (l == r)
    {
        scanf("%d",&a[rt]);
        b[rt] = a[rt];
        return ;
    }
    int m = (l + r)>>1;
    build(lc);
    build(rc);
    pushup(rt);
}
int get(int L,int R,int l,int r,int rt,int mk)
{
    if (l >= L && r <= R)
    {
        if (!mk) return b[rt];
        else return a[rt];
    }
    int m = (l + r)>>1;
    int res = mk == 0 ? inf:-inf;
    if (L <= m)
    {
        if (!mk) res = min(res,get(L,R,lc,mk));
        else res = max(res,get(L,R,lc,mk));
    }
    if (R > m)
    {
        if (!mk) res = min(res,get(L,R,rc,mk));
        else res = max(res,get(L,R,rc,mk));
    }
    return res;

}


int main()
{
    //freopen("din.txt","r",stdin);
    int n,k,i;
    scanf("%d%d",&n,&k);
    build(1,n,1);
     int s = 1,e = k;
     int len = 0;
    for (i = k; i <= n; ++i)
    {
        ans1[len] = get(s,e,1,n,1,0);
        ans2[len++] = get(s,e,1,n,1,1);
        s++,e++;
    }
    for (i = 0; i < len - 1; ++i)
    printf("%d ",ans1[i]);
    printf("%d\n",ans1[i]);

    for (i = 0; i < len - 1; ++i)
    printf("%d ",ans2[i]);
    printf("%d\n",ans2[i]);


    return 0;
}

 

rmq代码:

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)

#define ll long long
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 1000007
#define N 5007
#define M 200007
using namespace std;

int f[maxn],val[maxn];
int n,k;
void initMIN()
{
    int i,j,m;
    for (i = 1; i <= n; ++i) f[i] = val[i];
    m = log(1.0*k)/log(2.0);
    for (i = 1; i <= m; ++i)
    {
        for (j = 1; j + (1<<i) - 1 <= n; ++j)
        f[j] = min(f[j],f[j + (1<<(i - 1))]);
    }
}
void initMAX()
{
    int i,j,m;
    for (i = 1; i <= n; ++i) f[i] = val[i];
    m = log(1.0*k)/log(2.0);//我们只要求得m即可f[j]存储的是从j开始长度为2^m次方的最值
    for (i = 1; i <= m; ++i)
    {
        for (j = 1; j + (1<<i) - 1 <= n; ++j)
        f[j] = max(f[j],f[j + (1<<(i - 1))]);
    }
}
int rmq(int s,int e,int mk)
{
    int m = log(1.0*k)/log(2.0);
    if (mk == 1) return max(f[s],f[e - (1<<m) + 1]);
    else return min(f[s],f[e - (1<<m) + 1]);
}
int main()
{
    //freopen("din.txt","r",stdin);
    int i;
    scanf("%d%d",&n,&k);
    for (i = 1; i <= n; ++i) scanf("%d",&val[i]);

    initMIN();
    int s,e;
    s = 1; e = k;
    for (i = k; i <= n; ++i)
    {
        if (i != n) printf("%d ",rmq(s,e,0));
        else printf("%d\n",rmq(s,e,0));
        s++; e++;
    }
    initMAX();
    s = 1; e = k;
    for (i = k; i <= n; ++i)
    {
        if (i != n) printf("%d ",rmq(s,e,1));
        else printf("%d\n",rmq(s,e,1));
        s++; e++;
    }
    return 0;
}

 

hdu3415 Max Sum of Max-K-sub-sequence

 http://acm.hdu.edu.cn/showproblem.php?pid=3415

题意:

给你一个环游N个元素组成,求最大的最多有k个连续元素的子串的最大和,起点肯定为1-n,我们在处理的复制n以后的k个来处理;

思路:

单调队列维护k长度区间内1-i的和的最小值,而我们要枚举的区间的和,就是在满足k的前提下,a[i] - a[q[front] - 1] a[i]表示1-i的区间和。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)

#define ll long long
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define N 5007
#define M 200007
using namespace std;

int q[2*maxn];
int a[2*maxn];
int n,k;

int main()
{
    //freopen("din.txt","r",stdin);
    int i,t;
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d",&n,&k);
        a[0] = 0;
        for (i = 1; i <= n; ++i)
        {
            scanf("%d",&a[i]);
            a[i] += a[i - 1];
        }
        for (i = n + 1; i <= n + k; ++i)//把从n以后的k个复制
        a[i] = a[n] + a[i - n];
        int front = 0,tail = -1;
        int MAX = -inf;
        int s = 0,e = 0;
        for (i = 1; i <= n + k; ++i)
        {
            while (tail >= front && i - q[front] >= k) front++;//单调队列维持其维护的长度

            while (tail >= front && a[q[tail] - 1] > a[i - 1]) tail--;//其存储1-i的和最小的
            q[++tail] = i;

            if (a[i] - a[q[front] - 1] > MAX)//每次计算一下最值
            {
                MAX = a[i] - a[q[front] - 1];
                s = q[front];
                e = i;
                if (e > n) e -= n;
            }
        }
        printf("%d %d %d\n",MAX,s,e);
    }
    return 0;
}

 

hdu: Subsequence

http://acm.hdu.edu.cn/showproblem.php?pid=3530

http://hi.baidu.com/fhnstephen/blog/item/9e908215d2694415962b4331.html这里的解题思路比较详细,注意这里他说如果队尾元素和当前元素相等,可以照样删除,因为同样的元素只要有一个就行了,而且是最后的一个。而我做的时候是留下的觉得如果遇到相同的最小值,我们要用最小值的最小坐标来计算最大的长度。比如如果最小值为2 有两个坐标分别为1 2 最大值坐标为7我们肯定用7 - 1来获得最大长度。ps:这里的子序列必须是连续的。。

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>

#define CL(a,num) memset((a),(num),sizeof(a))
#define iabs(x)  ((x) > 0 ? (x) : -(x))
#define Min(a,b) (a) > (b)? (b):(a)
#define Max(a,b) (a) > (b)? (a):(b)

#define ll long long
#define inf 0x7f7f7f7f
#define MOD 100000007
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
#define pi acos(-1.0)
#define test puts("<------------------->")
#define maxn 100007
#define M 100007
#define N 100007
using namespace std;

int a[N];
int q1[N],q2[N];

int main()
{
    //freopen("din.txt","r",stdin);
    int n,m,k,i;
    while (~scanf("%d%d%d",&n,&m,&k))
    {
        for (i = 1; i <= n; ++i) scanf("%d",&a[i]);

        int ans = 0, now = 0;
        int h1 = 0, h2 = 0;
        int t1 = 0,t2 = -1;
        for (i = 1; i <= n; ++i)
        {
            while (t1 >= h1 && a[q1[t1]] < a[i]) t1--;
            q1[++t1] = i;

            while (t2 >= h2 && a[q2[t2]] > a[i]) t2--;
            q2[++t2] = i;

            while (a[q1[h1]] - a[q2[h2]] > k)
            {
                if (q1[h1] < q2[h2]) now = q1[h1++];
                else now = q2[h2++];
            }
            if (a[q1[h1]] - a[q2[h2]] >= m && a[q1[h1]] - a[q2[h2]] <= k)
            ans = max(ans,i - now);
        }
        printf("%d\n",ans);
    }
    return 0;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值