2018-03-18 PAT 春季考试

今天下午 1:30 - 4:30,PAT 甲级考试,也是今年秋季之前的最后一次考试机会了。

我在离考试结束还有 22 分钟时拿到了 100 分,出考场的时候老师问我多少分,要不要等证书。我说 100 分,感受到了各位考生的眼神投过来。。。

现在趁还没忘完,回顾一下这几道题是怎么写的。

03 月 19 日更新:发现考试平台可以查看自己考试时提交的代码,于是也摘下来咯。考试时间紧,拿分是最重要的事,代码难免有很丑的地方,请各位看官包涵哈。

A 20分 20分钟

只用了 20 分钟,一次通过。

很奇葩的题,有些地方我觉得有坑,但是没管,竟然过了。给一个数字 D,然后让你输出这个数字第多少次的表示方法,比如 D 是 8,第一次说成 "81",就是 8 有 1 个,第二次 “8111”,8 有 1 个,1 有 1 个;第三次 “8113” 这样子。

我上来就是用字符串处理的,字符串开到最大,然后一点一点比,后一个数跟前一个不一样就输出前面的数字及个数。写的时候突然想到如果 1 连续出现了 >= 10 次怎么办,输出 “1xx” 吗?没管这个,交上去,没试几次,过了。。。

# include 
# include 

char s[1000000];
char s0[1000000];
char d;
int N;


int main(){
    int dt;
    scanf("%d %d", &dt, &N);
    d = '0' + dt;
    s[0] = d; s[1] = '\0';

    char lastc; 
    int lastn, idxs0;
    for(int i = 2; i <= N; i ++){
        lastc = s[0];
        lastn = 1;
        idxs0 = 0;
        for(int j = 1; ; j ++){
            if(s[j] != lastc){
                if(s[j] == '\0'){
                    s0[idxs0 ++] = lastc;
                    s0[idxs0 ++] = lastn + '0';
                    s0[idxs0 ++] = '\0';
                    break;
                }else{
                    s0[idxs0 ++] = lastc;
                    s0[idxs0 ++] = lastn + '0';
                    lastc = s[j];
                    lastn = 1;
                }
            }else{
                lastn ++;
            }
        }
        strcpy(s, s0); 
    }

    printf("%s", s);

    return 0;
}

B 25分 40分钟

给许多个“考生 分数 学校”,要通过它的要求计算出各个学校的 XXX(该学校学生的 顶级成绩和 * 1.5 + 甲级成绩 + 乙级成绩 / 1.5) 和 Ns (考试人数),然后对各个学校排名次,XXX 一样的同名次,这些的输出顺序再按照 Ns 和 学校名字典顺序决定。

学校的各项分数信息当然用结构体就可以了。很烦的就是学校名字符串怎么处理,我为了查找校名快,用了很暴力的办法,把学校名和学校信息的 struct 对应起来:

  • 一个 map, Key 是校名字符串,Value 是学校信息结构体的 idx (刚开始想用索引表示,后来发现一旦排序就乱了,又在结构体中加了一个字段 idx);

  • 后来发现自己不会从 map 中按照 value 查找值,尴尬了,只好又存了一个校名数组 schName[n][7],存 idx 对应的校名。很繁琐很丑,但是为了一次就迅速通过,我忍了。。。

这道题我提交了约 4~5 次才通过,总是漏掉一些奇怪的细节条件。我看到提交列表里,第一题的通过率是 0.2 左右,这道题则只有惊人的 0.07……还好我试了几次就通过了,这时考试才过去 1 个小时。

# include 
# include 
# include 
# include 
# include 

using namespace std;

typedef map str_int_map;
str_int_map indexmap;

struct schinfo{
    int idx;
    int scoreT, scoreA, scoreB;
    int ns;
    int TWS;
}schs[100000];

char schName[100000][7];

int N;
int indexm = 0;

int compar(const void* a, const void *b){
    schinfo *aa = (schinfo*)a, *bb = (schinfo*)b;
    if(aa->TWS != bb->TWS){
        return bb->TWS - aa->TWS;
    }else if(aa->ns != bb->ns){
        return aa->ns - bb->ns;
    }else{
        return strcmp(schName[aa->idx], schName[bb->idx]);
    }
}

int main(){
    scanf("%d", &N);
    char id[7], school[7];
    string sch;
    int score;

    str_int_map::iterator iterf;
    int idx;

    for(int i = 0; i < N; i ++){
        scanf("%s %d %s", id, &score, school);
        for(int j = 0; school[j] != '\0'; j ++){
            if(school[j] >= 'A' && school[j] <= 'Z'){
                school[j] = school[j] - 'A' + 'a';
            }
        }
        sch = school;

        iterf = indexmap.find(sch);
        if(iterf == indexmap.end()){ //not have
            idx = indexm;
            schs[idx].idx = idx;
            indexmap[sch] = indexm ++;
            strcpy(schName[idx], school);
        }else{ //already in 
            idx = iterf->second;
        }

        schs[idx].ns ++;
        if(id[0] == 'T'){
            schs[idx].scoreT += score;
        }else if(id[0] == 'A'){
            schs[idx].scoreA += score;
        }else{
            schs[idx].scoreB += score;
        }
    }

    for(int i = 0; i < indexm; i ++){
        schs[i].TWS = schs[i].scoreT * 1.5 + schs[i].scoreA + schs[i].scoreB / 1.5; //ScoreB/1.5 + ScoreA + ScoreT*1.5
    }

    qsort(schs, indexm, sizeof(schinfo), compar);

    printf("%d\n", indexm);
    int rank = 0;
    str_int_map::iterator iter;
    for(int i = 0; i < indexm; i ++){
        if(i == 0 || schs[i].TWS < schs[i - 1].TWS){ //new rank
            rank = i + 1;
        }
        
        printf("%d %s %d %d\n", rank, schName[schs[i].idx], schs[i].TWS, schs[i].ns);
    }

    return 0;
    
}

C 25分 30分钟

出考场的时候忘掉了这道题。过了一天才想起来。

其实还是要动动脑子的。刚看到这道题说 subset 和 clique 懵了一下,隐约记得《算法导论》上说过这个问题,好像是 NP 什么的,也没写过算法。结果一看题,只是让判断,没有让求。给一张图和几次查询,要求对于每次查询,给出这几个 vertice 组成的 subset 是否是个 clique,是否是个 maximal clique。

A clique is a subset of vertices of an undirected graph such that every two distinct vertices in the clique are adjacent. A maximal clique is a clique that cannot be extended by including one more adjacent vertex.

图建好很简单,然后就是对于每次查询的子集,判断是否两两连通,再判断是否有某个子集外的节点与子集中的每一个顶点连通,就知道是否是最大的clique 了。

# include 

bool graph[210][210];

int nv, ne;

int m;
int nset, set[210];
bool markver[210]; // if in set

int ans;

void printans(int answer){
    if(answer == 0){ //not max
        printf("Not Maximal\n");
    }else if(answer == 1){
        printf("Yes\n");
    }else{
        printf("Not a Clique\n");
    }
}

int main(){
    scanf("%d %d", &nv, &ne);
    int a, b;
    for(int i = 0; i < ne; i ++){
        scanf("%d %d", &a, &b);
        graph[a][b] = graph[b][a] = 1;
    }

    scanf("%d", &m);
    for(int i = 0; i < m; i ++){ // m query
        for(int j = 1; j <= nv; j ++){
            markver[j] = false;
        }
        scanf("%d", &nset);
        for(int j = 0; j < nset; j ++){
            scanf("%d", set + j);
            markver[set[j]] = true;
        }

        if(nset == 1){ //only one vertice in this subset
            bool alone = true;
            for(int j = 1; j <= nv; j ++){
                if(graph[set[0]][j]) {alone = false; break;}
            }
            if(alone) ans = 1; else ans = 0;
        }else{ // 2 or more vertices
            bool isclique = true;
            for(int j = 0; isclique && j < nset; j ++){ //every pair of vertices
                for(int k = j + 1; k < nset; k ++){
                    if(! graph[set[j]][set[k]]){
                        isclique = false;
                        break;
                    }
                }
            }
            if(! isclique){
                ans = -1;
            }else{ // is a clique, check if is max
                bool ismax = true, alladj = true;
                for(int k = 1; ismax && k <= nv; k ++){
                    if(!markver[k]){ // k is now not in this set
                        alladj = true;
                        for(int kk = 0; kk < nset; kk ++){
                            if(!graph[k][set[kk]]){
                                alladj = false;
                                break;
                            }
                        }
                        if(alladj){ //k is adj with every set[kk]
                            ismax = false;
                        }
                    }
                }
                if(! ismax) ans = 0; else ans = 1;
            }
        }
        printans(ans);
    }

    return 0;
}

D 二叉树 综合

开始写这道题的时候还有 80 分钟左右吧,时间充足,我仍然很紧张,毕竟前面才 70 分,万一写不出来就还是挺难看的。

这道题先给定一个前序遍历序列。我看过这样的题(虽然没写过),通过前序遍历序列,根据左孩子小右孩子大,能直接分出来根节点的左右子树,然后就是递归下去了。这次递归函数写得比较好看,一遍就能跑对了。函数签名大概是void build(node* pos, char lr, int lo, int hi),是把preorder[lo, hi)这一部分的子树放到 pos 的左孩子或者右孩子,用一个字符指示。函数里安上根节点(preorder[lo]),找到其左右字树的分界线,有子树的话就递归地挂那一部分。一棵树完成。

后面给了好多组查询,给两个 key a, b,查找 a 和 b 的最低公共祖先节点。当然,还要分 a 或者 b 不在这棵树里的情况,另外输出(这里写了个 node *search(int key))。如果两个 key 一个是另一个的祖先,也要识别出来。我本来想着把两个节点各自往上走,祖先存起来,然后两组祖先对比着查找,后来觉得太蠢了,万一树不平衡,祖先很长,查死你不偿命。

灵光一闪,想到之前写树的那篇文章里有学过 [dtime, ftime] 如果有包含关系,就存在祖先关系。于是给结构体增加了 dtime 和 ftime 两个字段,先做了一遍 dfs。之后就特别爽了,先看两个点的[dtime, ftime]数组是否包含就知道是否有祖先关系了。没有的话,顺着 a 往上找各个祖先,直到该祖先的[dtime, ftime]也包含了 b 的[dtime, ftime]。线性的复杂度哦!

这时还有将近半个小时考试结束,我有一个 case 没过,99 分,排第 23 名(从名单的页数来看是全国的名次)。自己试了几个 case,发现如果这两个节点一样,我给错判断了,应该认为 a 是 a 的祖先。加了几处等号,过了。走人啊嗨 ~~~

这道题的代码不丑!递归我写的很认真的!

# include 

struct node{
    int data;
    node *p, *lc, *rc;
    int dtime, ftime; //discovered and finished time
};

int pre[100100];
node *root;
int m, n; //n = nkey, m = mquery

void insertAsRoot(int key){
    root = new node;
    root->lc = root->rc = root->p = NULL;
    root->data = key;
}

node *insertAslc(int key, node *pos){
    pos->lc = new node;
    pos->lc->lc = pos->lc->rc = NULL;
    pos->lc->p = pos;
    pos->lc->data = key;
    return pos->lc;
}

node *insertAsrc(int key, node *pos){
    pos->rc = new node;
    pos->rc->lc = pos->rc->rc = NULL;
    pos->rc->p = pos;
    pos->rc->data = key;
    return pos->rc;
}

void build(node* pos, char lr, int lo, int hi){
    node *x;
    if(lr == 'l'){
        x = insertAslc(pre[lo], pos);
    }else{
        x = insertAsrc(pre[lo], pos);
    }
    if(hi - lo == 1){
        return;
    }
    int part = lo + 1;
    for(; part < hi; part ++){
        if(pre[part] > pre[lo]){
            break;
        }
    }
    if(part < hi){ //have right tree
        build(x, 'r', part, hi);
    }
    if(part > lo + 1){ //have left tree
        build(x, 'l', lo + 1, part);
    }
}

node *search(int key, node *pos){
    if(pos == NULL || pos->data == key){
        return pos;
    }
    if(key < pos->data){
        return search(key, pos->lc);
    }else{
        return search(key, pos->rc);
    }
}

void dfs(node *pos, int &clk){
    if(pos == NULL) return;
    pos->dtime = clk ++;
    dfs(pos->lc, clk);
    dfs(pos->rc, clk);
    pos->ftime = clk ++;
}

int main(){
    scanf("%d %d", &m, &n);
    for(int i = 0; i < n; i ++){
        scanf("%d", pre + i);
    }

    insertAsRoot(pre[0]);
    int part =1;
    for(; part < n; part ++){
        if(pre[part] > pre[0]){
            break;
        }
    }
    if(part < n){ //have right tree
        build(root, 'r', part, n);
    }
    if(part > 1){ //have left tree
        build(root, 'l', 1, part);
    }

    int clk = 0;
    dfs(root, clk);

    int a, b;
    node *afound, *bfound;
    for(int i = 0; i < m; i ++){ // m queries
        scanf("%d %d", &a, &b);
        afound = search(a, root);
        bfound = search(b, root);
        if(!afound && !bfound){
            printf("ERROR: %d and %d are not found.\n", a, b);
        }else if(!afound){
            printf("ERROR: %d is not found.\n", a);
        }else if(!bfound){
            printf("ERROR: %d is not found.\n", b);
        }else{ //look for their ancestors
            if(afound->dtime <= bfound->dtime && bfound->ftime <= afound->ftime){ // a is ance of b
                printf("%d is an ancestor of %d.\n", a, b);
            }else if(bfound->dtime <= afound->dtime && afound->ftime <= bfound->ftime){ // b is ance of a
                printf("%d is an ancestor of %d.\n", b, a);
            }else{
                node *ance = afound->p;
                while(ance){
                    if(ance->dtime < bfound->dtime && ance->ftime > bfound->ftime){ //[dtime, ftime] being included
                        printf("LCA of %d and %d is %d.\n", a, b, ance->data);
                        break;
                    }
                    ance = ance ->p;
                }
            }
        }
    }


    return 0;
}

你可能感兴趣的:(2018-03-18 PAT 春季考试)