[2014 Regional]鞍山 B Chat 做题实录

这次可就轮到我们队现场搞了……

回顾一下前面的情况:

首先是翻整本题册,8min的时候队友提出来看板,一下子发现I题很快FB就没了,然后马上跟风下去搞I,队友去看另一个有过的E,很快也搞定了

然后漫长的卡了D题4次,114min 5Y以后准备继续跟榜搞C,结果怎么想都没想出来平白无故浪费无数时间gg……

然后看板,发现B题有6个过了,看了一下B,很多操作的沙茶模拟,队友同时看了K和H,表示这个另外的题目搞定比搞B麻烦,于是我马上下手搞B题了。

一开始读完题,n^2的暴力很快就能出来的,但是我一开始把Top和Untop的意义理解错了,理解成队列的操作,后来一发Clarification问出去,返回Read Again后再看,一下子懂了——Top本质是一个特别的指针,一直指向队列里优先级为u的那个。


B题首先读完操作要对这些操作归类(模拟题做多了这种会注意起来的,繁杂的规则中整理出一些共性的东西)

1、增删和改值——Add、Close、Chat

2、维护Top指针——Top、Untop

3、队列里抽调某一个——Rotate、Choose、Prior


第一组肯定是首先实现的,不然后面的没法调试(不过老实说,时间紧张,只有写完了才会去调试吧……)


然后考虑第二组。

Top指针 记录优先级u  好还是 记录优先级为u的在队列里是哪一个 比较好?

当时也没细想,一拍大腿,选择记录优先级为u的在队列里是哪一个——这样埋下了出错的隐患

现在有时间了,仔细讨论一下这个细节:
如果记录优先级u:
好处:不需要在第三组和上面的Close操作的时候维护一下Top指针的指向
坏处:Chat的时候很可能需要O(n)找到具体对哪一个chat
如果选择记录优先级为u的在队列里是哪一个的话,好处和坏处对调一下。
现在比赛打完,回头仔细想,其实记录优先级u更好。
维护优先级为u的在队列里哪一个的问题是,队列里发生成片的移动的时候你需要同时维护一下,至少2个地方要特别注意,容易新增错误还很隐晦。
——特别是Close操作那里,选择记录优先级u的话要省心很多,Close和top那个一样优先级的时候直接作废。
如果是记录u优先级是哪一个,真的去指向的话,除了可能作废,还有队列后面的提前的情况发生,很讨厌。
(不过我实在是太懒了,下面贴的过了的代码还是记录优先级为u的在队列中的哪个位置的)

最后第三组。
这三个操作的共同特点是,都需要把队列里某个位置的提前,抽出共同需要的这个操作写成单独一个函数(这是一个正确的决定)。

同时还发现,找队列里优先级为u的元素也是一个使用的地方很多的操作,同样的也单独写成一个函数。

最后注意到样例输出有个Bye 3: 2,看看操作后对Bye的说明:
关闭在顶端的窗口,同时对每个打开的窗口之前说过话的再Say Bye,没说过话的不要去Bye了。(这里是个理解错误比较多的地方)
回忆一下电脑上关窗口的情况:永远置顶的先关掉,关完以后从上往下关
那这里同理,先关top指针指向的那个窗口,然后再按队列从前往后管关了。

现在整理一下:
首先是单独两个函数,一个找队列里优先级为u的元素,另一个把把队列里某个位置的元素提前。
之后是读入操作,按上面的3类操作在一块写。

然后就开始敲吧……
敲啊敲啊敲啊敲……
我都不知道自己敲了多久,终于敲完了……
然后数据打进去,本地测试跑一下,修了几个nc的bug,看上去本地样例无误了,提交,结果是WA,还是预料之内的,WA一下很正常。
然后打印了2份,很快送来了,和一个队友一起离线对着代码debug,然后发现了2处错误。
下面把现场赛打印的代码的照片发上来(图片巨大,上面打了草稿望谅解,上面打了2个出错的地方gg)
[2014 Regional]鞍山 B Chat 做题实录_第1张图片
[2014 Regional]鞍山 B Chat 做题实录_第2张图片
[2014 Regional]鞍山 B Chat 做题实录_第3张图片

然后和队友一起看了1遍,2遍,3遍……(另一个队友在敲H,可他敲完以后发现看错题了GG……),感觉没问题了,把另一个队友赶下来,修改不超过50B,提交,218min 2Y也是醉了。

最后贴一个完整的过题代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <iostream>
#include <algorithm>
using namespace std;

struct Girl
{
    int id;
    long long words;
}queue[10005];
int qtail;
int topid;

int find(int u)
{
    for(int i=0;i<qtail;i++)
        if(queue[i].id==u)
            return i;
    return -1;
}

void movetotop(int x){
    int topu=queue[topid].id;
    Girl tmp=queue[x];
    for(int i=x;i>0;i--){
        queue[i]=queue[i-1];
        if(queue[i].id==topu)
            topid=i;
    }
    queue[0]=tmp;
    if(queue[0].id==topu)
        topid=0;
}

int main()
{
    int T;
    int op;
    char t[15];
    int id;
    for(scanf("%d",&T);T--;){
        scanf("%d",&op);
        qtail=0;topid=-1;
        for(int O=1;O<=op;O++){
            scanf("%s",t);
            printf("Operation #%d: ",O);
            if(strcmp(t,"Add")==0){
                scanf("%d",&id);
                if(find(id)>=0){
                    printf("same priority.\n");
                    continue;
                }
                queue[qtail].id=id;
                queue[qtail].words=0;
                qtail++;
                printf("success.\n");
            }else if(strcmp(t,"Close")==0){
                scanf("%d",&id);
                int pl=find(id);
                if(pl>=0){
                    printf("close %d with %I64d.\n",id,queue[pl].words);
                    for(int i=pl;i<qtail;i++)
                        queue[i]=queue[i+1];
                    qtail--;
                    if(pl==topid) topid=-1;
                    else if(pl<topid) topid--;
                }
                else{
                    printf("invalid priority.\n");
                }
            }else if(strcmp(t,"Chat")==0){
                scanf("%d",&id);
                if(qtail==0){
                    printf("empty.\n");
                    continue;
                }
                if(topid>=0)
                    queue[topid].words+=id;
                else
                    queue[0].words+=id;
                printf("success.\n");
            }else if(strcmp(t,"Top")==0){
                scanf("%d",&id);
                int pl=find(id);
                if(pl>=0){
                    printf("success.\n");
                    topid=pl;
                }
                else
                    printf("invalid priority.\n");
            }else if(strcmp(t,"Untop")==0){
                if(topid>=0){
                    printf("success.\n");
                    topid=-1;
                }
                else
                    printf("no such person.\n");
            }else if(strcmp(t,"Rotate")==0){
                scanf("%d",&id);
                if(id<=qtail){
                    printf("success.\n");
                    movetotop(id-1);
                }
                else
                    printf("out of range.\n");
            }else if(strcmp(t,"Choose")==0){
                scanf("%d",&id);
                int pl=find(id);
                if(pl>=0){
                    printf("success.\n");
                    movetotop(pl);
                }
                else{
                    printf("invalid priority.\n");
                }
            }else if(strcmp(t,"Prior")==0){
                if(qtail==0){
                    printf("empty.\n");
                    continue;
                }
                int maxid=0;
                for(int i=1;i<qtail;i++)
                    if(queue[i].id>queue[maxid].id)
                        maxid=i;
                printf("success.\n");
                movetotop(maxid);
            }
        }
        if(topid>=0&&queue[topid].words>0){
            printf("Bye %d: %I64d\n",queue[topid].id,queue[topid].words);
            queue[topid].words=0;
        }
        for(int i=0;i<qtail;i++){
            if(queue[i].words>0)
                printf("Bye %d: %I64d\n",queue[i].id,queue[i].words);
        }
    }
    return 0;
}

【有关Chat一题的一些有趣了解】
0、这个版块和thu没关系
1、我们学校一个队伍非常奔放的看到那些操作,数据范围都没管敲了一棵splay,敲完后发现n不大,硬着头皮继续搞而且封板后5min 245min 3Y,服了
2、zstu的wyq大神的队伍把题目扔给了队友,队友把rotate操作读错了,见名思义,想成了STL的rotate,听到表示GG……
3、待大家的贡献以补充了

【总结】
做模拟题,看起来套路就是:
先把题读完、读对、读好,条件整理一下,然后该设计的结构体设计出来,常用的一些基本操作抽象成函数写出来
然后实现的时候不要作死,逻辑越简单粗暴越好,太早的优化是万恶的源泉(当然那种不让你AK的压轴模拟题除外)
打完,过样例以后提交上去出错很正常,不要慌,根据题目灵活选择调试办法,要不机器上构造感觉逻辑处理不到位的边角数据,要不代码搞下来,自己读,找手滑错误和逻辑错误,当然两个队友一起最好
不过说老实话,模拟题这种,平时就应该有人专门针对的去搞一两下,快速并准确的读题,了解需要的细节,然后耐心又准确的实现,打的时候不用太快但一定要让思路和手跟上以减少莫名其妙的遗忘和其他错误,练多了准确率和过题速度就会上去了

不过看来以后我们队这种题真的不应该太怂的,毕竟是自己的优势项目,怂了莫名其妙亏罚时落后下来还是蛮郁闷的。

你可能感兴趣的:(Algorithm,regional)