计蒜客-----The 2018 ACM-ICPC Chinese Collegiate Programming Contest比赛第一题-----题目解析 注意事项

题目链接:https://nanti.jisuanke.com/t/28401

题目大意:

现在有一个栈,初始为空。接下来有若干次操作,每次可能向栈顶 push 一个正整数,也可能 pop 掉 栈顶元素。
你需要在每次操作之后计算出栈内所有元素的最大值。如果栈为空则认为此时最大值是 0。 为了避免输入文件过大,所有操作会使用 rng61 算法生成。同时为了避免输出文件过大,你只需要 输出一个数:表示每次操作之后的答案与下标乘积的异或和。 

输入格式:

第一行包含一个整数 T,表示测试数据的组数。
接下来依次描述 T 组测试数据,为了减少读入量,采用如下的方式来给出数据。
对于每组测试数据:一行给出了 7 个整数依次为 n,p,q,m,SA,SB,SC。其中 n 表示总的操作次数, 你的程序可以使用如下所示的 C++ 代码生成所有的操作。
(代码见原题面) 在这份代码中,PUSH(v) 表示向栈中插入一个新的元素,它的值为 v。POP() 表示弹出栈顶元素;如 果当前栈为空,则什么也不操作(即:没有东西可以弹出)。 

输出格式:

对于每组测试数据,输出一行信息 "Case #x: y"(不含引号),其中 x 表示这是第 x 组测试数据, y 等于 n ⊕i =1 (i·ai),其中 ai 表示第 i 次操作后的答案,⊕ 表示异或和。

题目解析:

题目本身并不是很难,但是坑点很多。

对于每次操作完成后,都要求找出栈中最大的元素,然后乘下标编号,求异或和。

常规解法:我们可以写3个函数,一个push函数,一个pop函数,还有一个getmax函数,getmax函数用于寻找栈中最大的元素

每次操作完成后,都进行一遍getmax找到栈中最大值。

但是这种解法交到oj上超时,显然不行。

正确解法:我们写两个函数,一个push函数,一个pop函数,我们保证栈中栈顶元素始终是栈中所有元素最大的,这样找最大值时就很方便了,直接top()一下就行了。如何做到这一点呢?我们每次加数的时候,判断一下要加的数和栈顶的数哪个更大,往栈中加入更大的那个。对于pop操作,正常进行就行。

对于pop操作,为啥可以正常进行呢?看图:

我们假设对栈的操作是:push( 1 ) , push( 3 ) , push( 2 ) , pop( ) , push( 7 ) ,push( 4 ) , pop( )

push( 1 ):

最大值---->1(栈顶元素)

push( 3 ):

最大值--->  3(栈顶元素)

push( 2 ):

最大值--->3  (栈顶元素)

pop( ):

最大值--->3 (栈顶元素)

push( 7 ):

 

最大值--->7(栈顶元素)

push( 4 ):

最大值--->7(栈顶元素)

pop( ):

最大值--->7(栈顶元素)

通过上面演示看,虽然改变了栈中的原始应该存入的数据,但是本题问的是栈中最大的元素,并不影响结果,所以这种方法可行。

代码如下:
 

#include<bits/stdc++.h>
using namespace std;
int n,p,q,m;
int t;
unsigned int SA,SB,SC;
stack< int >q1;
long long sum;//最后的答案必须储存在long long中,因为int可能放不开
unsigned int rang61()
{
    SA^=SA<<16;
    SA^=SA>>5;
    SA^=SA<<1;
    unsigned int t=SA;
    SA=SB;
    SB=SC;
    SC^=t^SA;
    return SC;
}
void PUSH(int l)
{
    if(q1.empty())//栈中没有元素时,不用比较,直接放进去
        q1.push(l);
    else
        q1.push(max(l,q1.top()));//我们确保栈顶元素是栈中所有元素中最大的,将l和栈顶元素进行比较,大的放入栈中,那么放入栈中的元素就是最终最大的了
}
void POP()
{
    if(q1.empty())//栈中没有元素可以删除
        return ;
    else
        q1.pop();
    return ;
}
int main()
{
    cin>>t;
    int t1=1;
    while (t--)
    {
        while (!q1.empty())//题目要求进行多组计算,那么每次计算前都得把栈中元素清空
            q1.pop();
        sum=0;
        cin>>n>>p>>q>>m>>SA>>SB>>SC;
        for(int i=1;i<=n;i++)
        {
            if(rang61()%(p+q)<p)
                PUSH(rang61()%m+1);
            else
                POP();
            if(q1.empty())//这个地方必须要判断,因为执行完上面的操作后,栈中可能没有元素了,如果直接q1.top(),肯定不对了
                sum^=1LL*i*0;
            else
                sum^=1LL*i*q1.top();
        }
        cout<<"Case #"<<t1++<<": "<<sum<<endl;
    }
}

题目心得(学会的东西):

1:在栈中找最大值:

方法一:通过数组将栈中所有元素取出来,然后找最大值,但是时间复杂度很高。

方法二:每次往栈中放元素时确保栈顶元素是栈中元素的最大值,但是这样会破坏栈的原始数据,如果害怕破坏栈的原始数据,可以在定义一个栈,用新定义的栈进行上述操作。

2:做题时,可以将数据设置成long long型,因为有可能int型放不开。(这个题int型就放不开,踩坑了  o(╥﹏╥)o   呜呜呜.....)

3:对于有多组操作的题目,进行完一组操作后,一定要把操作中所用的数据、容器都要清零,避免对下次操作产生影响。

猜你喜欢

转载自blog.csdn.net/qq_40938077/article/details/81099864