0x10基本数据结构

0x11 栈

栈是一种后进先出的线性数据结构

AcWing 41.包含min函数的栈

维护两个栈,一个记录栈的值,另一个单调栈,记录下当前的最小值即可
coding

AcWing 128. 编辑器

开两个栈维护,类似对顶堆的操作,我们把他叫做对顶栈好了

\(P\)为光标位置,分别开两个栈\(a,b\)

\(a\)\(P\)之前的数,栈\(b存\)P$之后的数

\(sum\)是前缀和,\(f\)是前缀和的最大值

对于操作\(L\),把\(x\)压入栈\(a\)并更新\(sum\)\(f\)

对于操作\(D\) ,栈\(a\)栈顶弹出

对于操作\(L\),把栈顶\(a\)弹出并压入栈\(b\)

对于操作\(R\),把栈顶\(b\)弹出并压入栈\(a\)同时更新\(sum\)\(f\)

对于操作\(Q\),返回\(f[x]\)

 #include <bits/stdc++.h>
using namespace std;


const int N = 1e6 + 5 , INF = 0x7ffffff;
int T , opt , a[N] , b[N] , sum[N] , f[N] , ta = 0 , tb = 0;

inline int read( bool _ )
{
    register int x = 0 , f_ = 1;
    register char ch = getchar();

    if( _ )
    {
        while( ch < '0' || ch > '9' )
        {
            if( ch == '-' ) f_ = -1;
            ch = getchar();
        }
        while( ch >= '0' && ch <= '9')
        {
            x = ( x << 3 ) + ( x << 1 ) + ch - '0';
            ch = getchar();
        }
        return x * f_;
    }
    else
    {
        while( ch != 'L' && ch != 'R' && ch != 'I' && ch != 'D' && ch != 'Q' ) ch = getchar();
        return int(ch);
    }
}

inline void work_1()
{
    a[ ++ ta ] = read(1);
    sum[ta] = sum[ ta - 1 ] + a[ta];
    f[ta] = max( sum[ta] , f[ ta - 1] );
    return ;
}

inline void work_2()
{
    if( ta > 0 ) ta --;
    return ;
}

inline void work_3()
{
    if( ta > 0 )b[ ++ tb] = a[ ta ] , ta --;
    return ;
}

inline void work_4()
{
    if( !tb ) return ;
    a[ ++ ta ] = b[tb];
    tb --;
    sum[ta] = sum[ta - 1] + a[ta];
    f[ta] = max( sum[ta] , f[ ta - 1] );
    return ;
}

inline void work_5()
{
    printf("%d\n",f[ read(1) ] );
    return ;
}


int main()
{
    f[0] = -INF;
    T = read(1);
    while( T -- )
    {
        opt = read(0);
        if(opt == 'I' ) work_1();
        else if(opt == 'D' ) work_2();
        else if(opt == 'L' ) work_3();
        else if(opt == 'R' ) work_4();
        else work_5();
    }
    return 0;
}

AcWing 131. 直方图中最大的矩形

画图手玩样例就能发现规律

单调栈的经典应用,不过我比较懒,STL+O2直接水过去

#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define LL long long
using namespace std;


const int N = 100005;
int n , now , width ;
LL res;
struct node
{
    int w , h;
}_;
stack< node > s;


inline int read()
{
    register int x = 0;
    register char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' )
    {
        x = ( x << 3 ) + ( x << 1 ) + ch - '0';
        ch = getchar();
    }
    return x;
}

inline node make( int x , int y )
{
    _.h = x , _.w = y;
    return _;
}


int main()
{
    while( 1 )
    {
        n = read();
        if( !n ) return 0;
        res = 0;
        for( register int i = 1; i <= n ; i ++ )
        {
            now = read();
            if( s.empty() || now > s.top().h ) s.push( make( now , 1 ) );
            else
            {
                width = 0;
                while( !s.empty() && s.top().h > now )
                {
                    width += s.top().w;
                    res = max( res , (LL)width * s.top().h );
                    s.pop();
                }
                s.push( make( now , width + 1 ) );
            }
        }
        width = 0;
        while( !s.empty() )
        {
            width += s.top().w;
            res = max( res , (LL)width * s.top().h );
            s.pop();
        }
        printf( "%lld\n" , res );
    }
    return 0;
}

0x12 队列

队列是一种“先进先出”的线性数据结构,手写队列时可以用循环队列来优化空间

队列还有一些变形体,优先队列,单调队列,双端队列,这些在\(STL\)中都是有的,不过常数比较大普通队列手写即可

另外优先队列在pbds中也有

AcWing 132. 小组队

这道题本身并不难,只是数据的处理比较恶心

首先开一个队列为维护小组,再开\(n\)个队列维护每个小组的成员

每次压入一个元素,就把这个元素加入这个小组的队列,如果这个小组的队列是空的就把他加入总的队列

每次弹出一个元素,就把总队列队头的小组弹出一个,如果队头小组的队列此时为空,就把队头小组从总队列总弹出

这道题并不是十分的卡常数,不开\(O2\)貌似能过,

另外插队不是好习惯,小心被打

#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;


const int N = 1e6 + 5 , M = 1005;
int n , t , m , num , cub[N];
string opt;
map< int , queue<int> > member;
queue< int > team;


inline int read()
{
    register int x = 0;
    register char ch = getchar();
    while( ch < '0' || ch > '9' ) ch = getchar();
    while( ch >= '0' && ch <= '9' )
    {
        x = ( x << 1 ) + ( x << 3 ) + ch - '0';
        ch = getchar();
    }
    return x;
}

inline void push()
{
    num = read();
    if( member[ cub[num] ].empty() ) team.push( cub[num] );
    member[ cub[num] ].push( num );
    return ;
}

inline void pop()
{
    num = team.front();
    printf( "%d\n" , member[ num ].front() );
    member[ num ].pop();
    if( member[ num ].empty() ) team.pop();
}

inline void work( int k )
{
    n = read();
    if( !n ) exit(0);
    printf( "Scenario #%d\n" , k );

    while( !team.empty() )
    {
        num = team.front();
        while( !member[ num ].empty() ) member[ num ].pop();
        team.pop();
    }
    memset( cub , 0 , sizeof(cub) );

    for( register int i = 1 ; i <= n ; i ++ )
    {
        t = read();
        while( t -- )  cub[ read() ] = i;
    }

    while( 1 )
    {
        cin >> opt;
        if( opt == "ENQUEUE" ) push();
        else if( opt == "DEQUEUE" ) pop();
        else break;
    }
    puts("");
    return ;
}


int main()
{
    for( register int k = 1 ; 1 ; k ++ ) work(k);
    return 0;
}

AcWing 135. 最大子序和

单调队列的基操

首先对于区间和的问题一般情况下都是转发乘前缀和数组,做差即可

然后就是找左右端点的问题

令前缀和数组为\(s\)

已经枚举的右端点\(i\)和当前的左端点\(j\)

此时再任意一个\(k\)如果满足\(k<j<i\)\(s[k]>s[j]\),着\(k\)无论如何也不可能成为最有解,因为对于任意的\(i\)如果可以选\(j\)\(j\)一定\(k\)更优

所以我们发现需要维护一个单调递增的序列,并且随着\(i\)的有移,将会有部分的\(j\)不能使用

符合单调队列的性质所以用单调队列来维护,队列储存的元素是前缀和数组的下标,队头为\(l\),队尾为\(r\)

对于每次枚举的\(i\)有以下几个操作

  1. 如果\(q[l] < i - m\)将队头出对
  2. 此时的\(l\)就是最有的\(j\)更新答案
  3. 维护单调队列性质并把\(i\)放入队列
#include <bits/stdc++.h>
using namespace std;


const int N = 300000;
int n , m , s[N] , q[N] , l  = 1 , r = 1 , res ;


inline int read()
{
    register int x = 0 , f = 1;
    register char ch = getchar();
    while( ch < '0' || ch > '9' )
    {
        if( ch == '-' ) f = -1;
        ch = getchar();
    }
    while( ch >= '0' && ch <= '9' )
    {
        x = ( x << 3 ) + ( x << 1 ) + ch - '0';
        ch = getchar();
    }
    return x * f;
}


int main()
{
    n = read() , m = read();
    for( register int i = 1 ; i <= n ; i ++ ) s[i] = s[i-1] + read();

    for( register int i = 1 ; i <= n ; i ++ )
    {
        while( l <= r && q[l] < i - m ) l ++;
        res = max( res , s[i] - s[ q[l] ] );
        while( l <= r && s[ q[r] ] >= s[i] ) r --;
        q[ ++ r ] = i;
    }
    cout << res << endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Mark-X/p/11257423.html
今日推荐