今天下午 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 <cstdio>
# include <cstring>
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<str, int>, Key 是校名字符串,Value 是学校信息结构体的 idx (刚开始想用索引表示,后来发现一旦排序就乱了,又在结构体中加了一个字段 idx);
后来发现自己不会从 map 中按照 value 查找值,尴尬了,只好又存了一个校名数组 schName[n][7],存 idx 对应的校名。很繁琐很丑,但是为了一次就迅速通过,我忍了。。。
这道题我提交了约 4~5 次才通过,总是漏掉一些奇怪的细节条件。我看到提交列表里,第一题的通过率是 0.2 左右,这道题则只有惊人的 0.07……还好我试了几次就通过了,这时考试才过去 1 个小时。
# include <cstdio>
# include <cstdlib>
# include <string>
# include <cstring>
# include <map>
using namespace std;
typedef map<string, int> 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 <cstdio>
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 <cstdio>
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;
}