SDNU -- 1296.PPMM

SDNU 1296.PPMM
http://www.acmicpc.sdnu.edu.cn/problem/show/1296

Description
假设这里有一个队列,我们可以对其进行下述操作:
PUSH X:意味着将一个整数X(-2^31<X<2^31)加入到队尾。
POP:从队头删除一个数,如果队列为空则不进行操作。
MINUS:将队列中所有的数字变成其相反数。
MAX:输出队列中所有数字的最大值,如果队列为空则不进行输出。

Input
第一行包括一个整数T(T≤10),表示接下来有T组测试数据。对于每一组测试数据,第一行包括一个数N(N≤2000000),表示操作次数,接下来的N行给出具体操作。

Output
对于每一组数据,在新的一行输出MAX返回的数。每组测试数据之间输出一个空行。

Sample Input
1
6
PUSH -2
MINUS
PUSH -1
MAX
POP
MAX

Sample Output
2
-1
Hint
数据结构题。队列的操作。


题目分析:

PUSH是把一个数放到队尾,POP是删除队首的一个数,这两个操作用QUEUE就可以解决。但是MINUS是将所有的数变成相反数,这样的话要遍历队列里的所有数变成相反数,这一点在QUEUE就不可以实现了,很显然要我们自己来模拟。变成相反数的话,如果遍历整个数组取反,次数少还好,但是次数多了就可能会超时,因此,我使用了两个数组来进行操作,一个数组输入正常值,另一个数组输入相反数,当出现MINUS的时候就交换主操作的数组。然而,这道题最大的问题不在这里,而是我连续输入好多数字之后要取最大值,如果我之前输入了最大值,然后发生了POP,恰好把我的最大值删除掉了,那我是否应该从头遍历一遍来再找一次最大值??很显然也会T,那我输入一个最大值并记录,再记录第二大的呢?当删除掉了最大的,又删掉了第二大的怎么办。对于这个问题,我采用了以下办法:从第一个数k开始,先记录j1为1,然后每输入一个数比k大时就覆盖掉k,同时j1加一,再输入一个数l比k小的时候,就将他输入到数组中k的后面,如果再输入一个比l大的数就覆盖掉l,同时有j2为1….当出现POP时j就减一,直到j1减为0的时候,第一段最大的数才会被删掉,接下来才会判断j2的减少……当输出MAX的时候就是把数组中的最前面的有效值输出。大体上是这么个意思。下面先放代码:再来一点一点分析。

代码如下:

#include 
#include 
#include 
#include 
#include 
using namespace std;
struct MMP
{
    struct operation
    {
        int value;
        int position;
    } ar[2000000];
    int vt, pt, vh, ph;
    void push(int n)
    {
        while(vt > vh && ar[vt - 1].value <= n)
            vt--;
        ar[vt].value = n;
        ar[vt].position = pt;
        vt++;
        pt++;
    }
    void pop()
    {
        if(vh < vt && ar[vh].position == ph)
        {
            vh++;
        }
        if(ph < pt)
            ph++;
    }
    int mx()
    {
        if(vh == vt)
            return -2147483648;
        else
            return ar[vh].value;
    }
    void r0()
    {
        vh = vt = ph = pt =0;
    }
} M[2];
int main()
{
    char str[10];
    int n, m;
    scanf("%d", &n);
    for(int i = 0; i < n; i++)
    {
        int num0 = 0, num1 = 1;
        M[0].r0();
        M[1].r0();
        if(i > 0)
        {
            printf("\n");
        }
        scanf("%d", &m);
        for(int j = 0; j < m; j++)
        {
            scanf("%s", str);
            if(strcmp(str, "PUSH") == 0)
            {
                int k;
                scanf("%d", &k);
                M[num0].push(k);
                M[num1].push(-k);
            }
            if(strcmp(str, "POP") == 0)
            {
                M[num0].pop();
                M[num1].pop();
            }
            if(strcmp(str, "MINUS") == 0)
            {
                int temp;
                temp = num0;
                num0 = num1;
                num1 = temp;
            }
            if(strcmp(str, "MAX") == 0)
            {
                int key;
                key = M[num0].mx();
                if(key != -2147483648)
                {
                    printf("%d\n", key);
                }
            }
        }
    }
    return 0;
}

我构建了一个结构体,这个结构体有两个数组,分别是输入原来的值和相反数的值。
结构体内还有一个结构体,这个结构体数组有200000个元素代表最多2000000次操作,这个结构体数组有两个值,分别是实际值和这个值在模拟数组中的真实位置。
外层结构体内有这样的输入、删除、取最大和初始化为0这四个操作。
输入PUSH时,会进行vt(实际值的结束点)向前查找,直到找到比他大的数的前一个ar[vt].value并替换掉,并且在ar[vt].position输入这个数的真实位置position。
输入POP时,就会先进行判断,如果当前删的已经达到了ar[vh].position的真实值位置与ph相同,那么现在删是局部最大值,vh就会加一,然后是进行日常判断ph加一。
输入MAX就是输出ar[vh]处的值,这个地方由于输入时要向前找最大值,因此这个vh存得永远是最大的,并且这个数列是递减数列。
最后一个是初始化。
MINUS我放在了主函数内,因为它比较好操作,每出现一次MINUS就主操作与副操作函数进行交换。


这道题大概R了4遍WA了15遍……真让人头大…..

笔者水平有限,如有错误,请指出……

你可能感兴趣的:(散题)