9501 ACMer不得不知道的事儿
时间限制:1000MS 内存限制:65535K 提交次数:0 通过次数:0
题型: 编程题 语言: 无限制
Description
作为一个入门的ACMer,在参加比赛之前,你势必要了解比赛的一些基本规则,例如一个叫Board的东西。 ACM赛制是8-11道题,5小时,以通过题数的多少以及耗时来排名。 比赛的时候有点像赛跑,因为不断有人提交题目,不断有人通过题目,所以,排名是不断变化的。 上一秒也许你还是第一名,下一秒也许你就是沙发了,再来可能板凳,甚至刷到看不见你的地方。 这也是竞赛的魅力所在,因为可以看到实时排名,所以不光是拼实力,还要拼心理承受能力拼策略等等。 有时候看着排名你会觉得鸭梨很大,头脑空白整个人当掉,那就完蛋了(所以Lyd比赛的时候是不看自己的名次的)。 但是不看排名呢?好吧,结束后你可能会说:“咦,E题很多人过,刚才没看,是啥题?啊,杀了我吧居然漏了个大水题-_-|||”总之,排名是个很重要的东西,下面我们来了解下。 排名也叫Ranklist,或者Board,通常会显示如下: 1、 按实时名次从高到低,显示所有队伍信息,每支队伍一行。 2、 信息包括过题数、罚时以及各题的提交情况。 3、 提交情况有该题提交次数以及通过时间(如果已通过)。 pc^2(ACM区域赛标准竞赛平台)生成的标准Board大概是这样的:
Solved是通过的题数,Time是罚时,ABCDEFGHIJ是对应题目编号的提交次数/通过时间,最后一个是总提交次数/通过题数。 罚时计算如下: 1、 总罚时为每道题目的罚时之和。 2、 每道题目的罚时=通过该题时的时间(即比赛开始了多久通过了该题)+20*该题提交不通过次数。 3、 未通过题目不计罚时。 4、 已通过题目再次提交,无论对错,不计罚时。 排名的规则是: 1、 题数多的在前。 2、 题数相同的计算罚时,罚时低的在前。 3、 题数罚时都相同的并列。(但为简单起见,本题要将这种情况的用strcmp比较队名,小的在前而不做并列) 为了让你能更好地了解比赛规则,现给出某场比赛的所有提交记录,请根据这些提交记录,来生成Board。 提交记录通常包括队名、提交时间、题号以及结果。 对于结果,并不是本题的所必须了解的,仍然希望大家有所了解,这对解决所有问题都有所帮助。常见的结果有如下几种: Complie Error (CE) :编译错误,所提交的代码在评测系统中无法编译为可执行程序。 比赛中如果出现这种结果,只要不是系统出问题,那绝对要把队友或者被队友拖出去毒打一顿的,属于“ACM五大天啊杀了我吧的理由”之一。 当然作为初学者经常会犯这样的错误,例如Ctrl+C的时候复制漏了最后一个花括号啊,该交Java交了GCC啊,将VC代码交到GCC的时候没有将__int64换成long long啊等等。 Runtime Error(RE) :运行时错误,程序在运行中发生错误,导致程序非正常结束(有时候主函数没有return 0;评测系统也会给出RE, 因为很多系统会将程序返回值作为程序是否正常退出的判断条件,所以将main定义成int main()并在结束的时候return 0;是个ACMer的好习惯)。 造成RE的,一般有整数或者浮点数除0、访问非法内存。除零错误就比较简单了,至于访问非法内存,大概就是数组越界, 例如数组开得太小(“ACM五大天啊杀了我吧的理由”之二,Lyd的队长Zjx在训练的时候经常干的事情)但又访问a[10000000]那样, 又或者操作空指针,又或者递归层数太多导致函数爆栈(递归层数和运行机器默认的栈空间大小以及函数形参的个数有关,当然通常1w层是有的)。 Memory Limit Exceeded(MLE) :超内存,程序运行时占用内存多于题目规定可以使用的内存。可见明显数组也不是开得越大越好, 一般来讲五百万的int数组是毫无鸭梨的,更多的话就要看题目所限的内存是多少。 Output Limit Exceeded(OLE) :输出超限制,程序运行时输出过多内容,远远超出标准答案所需要的长度,并且超出评测系统所限制的内容长度。 经典的bug是调试代码的时候,在代码里加了printf语句显示一些中间结果等神马信息,然后提交的时候忘了删掉。。。这杯具绝对是“ACM五大天啊杀了我吧的理由”之三。 Time limit Exceeded(TLE) :超时,程序在规定时间内未能结束,这时评测系统会将程序强行结束,并返回TLE。超时有两种,写烂了和想烂了。 不同人写的代码都是很大差别的,有些人显得特别快,很大程度上是因为某些代码上的差异,例如i++和i+=1时间上就有差别,只不过如果代码里少量用的话是体现不出来的, 除此之外有很多经验上的优化以及良好的代码习惯,就是大家所需要慢慢养成的了。 如果你自认为没写烂了代码,那么很大可能是想烂了,也就是算法不对,或者说算法效率低,解决的办法便是好好再想想。 Wrong Answer(WA) :错误,程序输出与标准答案不一致。这个结果最复杂,各种诡异都有,也是最为常见的错误。你所要做的,就是从想法到算法再到代码实现,都扫过一遍, 看看其中有没有bug,又有啥边界条件没考虑到,如果找到一些可以让你程序WA的数据什么的最好了。如果找了很久都发现不到有什么问题, 那么恭喜,很有可能你错在了一个很不起眼的地方,而且找到了会让你抓狂, 例如Lyd处于无脑状态下会将循环里面的某些i写成j或者j写成i(“ACM五大天啊杀了我吧的理由”之四)而被队友给予严重BS。 Presentation Error(PE) :格式错误,程序输出与标准答案一致,但格式不对。如果你得到的是这个结果,那么恭喜,你离通过该题不远了,同时,麻烦站墙角面壁三分钟去。 通常这类错误会在你没注意题目要求结果漏了在两组数据的输出之间输出空行或者输出多了空格或者空行的时候出现,毫无疑问“ACM五大天啊杀了我吧的理由”之五。 Accepted (AC) :通过,熬夜到四点切了这题,这辈子值了-_-|||
Input
数据的第一行是三个整数n(n<300),m(m<=12),k(k<3000),分别代表有n支队伍,m道题和k条记录或查询。 然后后面有n行,每行一个队名,由非空格字符组成。 接着k行,每一行首先是一个单词,submit或者show,若是show,请根据下面的输出格式,输出当前的Board; 若是submit,则表示这是一条提交记录,后面跟着是队名、提交时间、题号还有结果。队名长度不超过50。提交时间是0-300的整型数t,表示第t分钟提交, 结果则是"AC","WA","TLE","CE","RE","PE","MLE","OLE","OTHER"其中之一,其中AC代表通过该题,其余则表示各种错误。
Output
对每一条show,都要将所有队名的名次按从高到低输出。输出格式为: 第一行输出: rank name prosolved totaltime A B C D E … ABCDE是题目编号,根据题数的多少来输出,例如5题就输出到E,每一栏之间用空格分隔。 后面n行,每一行显示一支队伍的信息,按当前排名从高到低输出,如: 1 APM1000 1 30 1/30 2/- 1是rank,APM1000是队名,跟着的1是通过题数,30是罚时,1/30和2/-分别是A题提交1次并在30分的时候通过,以及B题提交两次,尚未通过。同样,用空格分开这些。
Sample Input
3 5 9 Arctic APM1000 Bumblebee submit APM1000 35 A WA submit Arctic 40 A AC submit Bumblebee 45 A AC submit APM1000 50 A AC submit Arctic 80 C WA submit Bumblebee 100 A WA submit Bumblebee 101 A WA submit Arctic 200 D WA show
Sample Output
rank name prosolved totaltime A B C D E 1 Arctic 1 40 1/40 0/- 1/- 1/- 0/- 2 Bumblebee 1 45 1/45 0/- 0/- 0/- 0/- 3 APM1000 1 70 2/50 0/- 0/- 0/- 0/-
Source
Lyd
Provider
a470086609
#include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct Question{ char name; //题名 int Submit; //提交次数 int AC; //通过的次数 }Question; typedef struct team{ char name[50]; int ACtotal; //作对的题数 int usetime; //总罚时 Question AllQuestion[12]; //每道题的情况 }team; int load(team queue[], int rank[], int n, int m, char alphabet[]) { int i, j, count = 0; printf("rank name prosolved totaltime"); for(i=0; i<m; ++i) printf(" %c", alphabet[i]); printf("\n"); for(i=0; i<n; ++i) { printf("%d ", ++count); printf("%s %d %d", queue[rank[i]].name, queue[rank[i]].ACtotal, queue[rank[i]].usetime); for(j=0; j<m; ++j) { printf(" %d/", queue[rank[i]].AllQuestion[j].Submit); if(!(queue[rank[i]].AllQuestion[j].AC)) printf("-"); else printf("%d", queue[rank[i]].AllQuestion[j].AC); } printf("\n"); } return 0; } int main() { team queue[300]; int i, j, l, p, q, t, n, m, k, k1, conveytime,term = 0, rank[300], part1, part2, part3, part4, temp; //n支队伍 m道题 k条记录 char *alphabet = "ABCDEFGHIJKL", conveyname, result[7], dui[50], choice[8]; scanf("%d%d%d", &n, &m, &k1); getchar(); for(i=0; i<n; ++i) { scanf("%s", queue[i].name); queue[i].ACtotal = queue[i].usetime = 0; for(j=0; j<12; ++j) { queue[i].AllQuestion[j].name = alphabet[j]; queue[i].AllQuestion[j].Submit = queue[i].AllQuestion[j].AC = 0; } } while(k1--) { scanf("%s", choice); if(strcmp("submit", choice) == 0) { scanf("%s", dui); i = 0; while(strcmp(queue[i].name, dui) != 0 && i<n) i++; scanf("%d", &conveytime); getchar(); conveyname = getchar(); j = 0; while(alphabet[j] != conveyname && j<m) j++; //查找题目名称,并计算最后的题目 scanf("%s", result); if(strcmp("AC", result) == 0) //判断提交是否为AC,是AC则计时 { if(queue[i].AllQuestion[j].AC == 0) { queue[i].AllQuestion[j].AC = conveytime; queue[i].usetime = queue[i].usetime + conveytime + queue[i].AllQuestion[j].Submit*20; queue[i].AllQuestion[j].Submit++; queue[i].ACtotal++; } } else if(queue[i].AllQuestion[j].AC == 0) queue[i].AllQuestion[j].Submit++; } else if(strcmp("show", choice) == 0) { memset(rank, 0, sizeof(rank)); for(i=0; i<n; ++i) rank[i] = i; part1 = part2 = 0; for(i=1; i<n; ++i) {//for 对AC题数排序 t = 0; for(j=0; j<n-i; ++j) { if(queue[(rank[j])].ACtotal < queue[(rank[j+1])].ACtotal) { t = 1; temp = rank[j+1]; rank[j+1] = rank[j]; rank[j] = temp; } } if(t == 0) break; } for(i=0, part1 = part2 = 0; i<n-1; ++i) {//对时间排序 if(queue[rank[i]].ACtotal == queue[rank[i+1]].ACtotal) part2 = i+1; if((i+1 == n-1) || (queue[rank[i]].ACtotal != queue[rank[i+1]].ACtotal)) { for(j=part1; j<part2; ++j) { for(k=j+1; k<=part2; ++k) { if(queue[rank[j]].usetime > queue[rank[k]].usetime) { temp = rank[j]; rank[j] = rank[k]; rank[k] = temp; } }//for k } for(part3=part4=l=part1; l<part2; ++l) { if(queue[rank[l]].usetime == queue[rank[l+1]].usetime) part4 = l+1; if((l+1 == part2) || (queue[rank[l]].usetime != queue[rank[l+1]].usetime) ) { for(q=0; q<part4-part3; ++q ) { for(p=part3; p<part4-q; ++p) // if(strcmp(queue[rank[p]].name, queue[rank[p+1]].name) > 0) { temp = rank[p]; rank[p] = rank[p+1]; rank[p+1] = temp; } } part3 = part4 = l+1; }//else }//part3 part1 = part2 = i+1; }//part2结束时间排序中不相等时的程序 }//for load(queue, rank, n, m, alphabet); }//else } return 0; }
解题报告:
就像题目所说的 basic and simular 一看这题就知道是个耗时间的苦力活,这题做了有大概两天的时间,具体是几个小时已忘了……WA[1]的原因是少了一对中括号!!!!!!!WA[2]的原因是忘了给一个变量赋值,看着两个原因,我心疼啊,不过……
1> 看清题目的条件,通过需要的数据自定义变量类型,队数和队名、题目数量、k条记录或查询、AC总题数、提交总时间、个体的提交次数和AC情况;
2> 通过Sample的Input编写代码,注意变量的初始化,初始化在输入队伍名称后进行,其中用已初始化为ABC……的字符串赋值给给个队伍的题目字符中,其余赋值为0;
3>根据题目意思,处理在队伍中的总罚时是否加时的情况,包括AC的时间、AC后再次提交、提交多次后未AC(不计时)、 AC前提交数次(*20);
4>队伍排序的问题, 三层排序:AC题数、罚时长短、 队名排序(基数排序);
5>输出处理。
这里介绍另外一种对多层排序的处理方法,比如说这题有三个关键字(AC题数、罚时长短、 队名排序)根据优先顺序应先根据AC题数排序(可用多种排序方法),在已进行一层排序的数组中,设定两个变量,以第一关键字为量化,根据第一关键字的异同初始化两个变量的值,这时两个变量之间所指的数具有相同的第一关键字,在此基础上将这段数组进行第二层排序,再次设定两个变量,以第二关键字为量化,根据第二关键字的异同初始化两个变量的值,这时两个变量之间所指的数具有相同的第二关键字,在此基础上将这段数组进行第三层排序,以此类推,多层排序结束后,继续回到第二层排序。