C语言程序设计100例之(76):ACM排名
例76 ACM排名
问题描述
ACM国际大学生程序设计竞赛是全球最具影响力的大学生程序设计竞赛,它以团队的形式代表各学校参赛,参赛队伍最多由三名参赛队员组成。
竞赛进行5个小时,一般有7道或以上试题,由同队的三名选手使用同一台计算机协作完成。当解决了一道试题之后,将其提交给评测机,由评测机判断其是否正确。若提交的程序运行不正确,则该程序将被退回给参赛队,参赛队可以进行修改后再一次提交该问题。程序判定结果有如下7种:
1)Accepted. --通过!(AC)
2)Wrong Answer.--答案错。(WA)
3)Runtime Error.--程序运行出错,意外终止等。(RE)
4)Time Limit Exceeded. --超时。程序没在规定时间内出答案。(TLE)
5)Presentation Error. --格式错。程序没按规定的格式输出答案。(PE)
6)Memory Limit Exceeded. --超内存。程序没在规定空间内出答案。(MLE)
7)Compile Error. --编译错。程序编译不过。(CE)
竞赛结束后,参赛各队以解出问题(AC)的多少进行排名,若解出问题数相同,按照总用时的长短排名。总用时为每个解决了(AC)的问题所用时间之和。一个已解决问题所消耗的时间是从竞赛开始到提交该问题的第一次被AC所用的时间,加上在被AC之前该问题的罚时(每次提交通不过,罚时20分钟)。没有解决的问题不记时。例如:A、B两队都正确完成两道题目,其中A队提交这两题的时间分别是比赛开始后1:00和2:45,B队为1:20和2:00,但B队有一题提交了2次。这样A队的总用时为1:00+2:45=3:45,而B队为1:20+2:00+0:20=3:40,所以B队以总用时少而获胜。
参与竞赛的团队根据解决问题的数量进行排名。解决相同数量问题的团队按总时间最少排序(虽然显示的时间以分钟为单位,但实际时间的测量精度为1秒,在对团队进行排名时会考虑秒数)。
根据上述规则,排名相同的团队则按团队编号从小到大进行排序。
编写程序,根据某次ACM竞赛中的N次提交情况,计算参加比赛的C个团队的排名表。
输入
输入第1行为整数C和N(1≤ C、N≤1000),
之后N行,每行由4个整数描述一次提交情况,这4个整数为ci、pi、ti和ri,其中:ci表示团队编号,pi表示问题编号,ti表示提交时间(以秒为单位),ri值为1或0,如果提交结果为AC,则为1,否则为0。(1 ≤ci ≤C,1≤pi≤20,1≤ ti≤36000)
输出
输出包含C个整数——按排名排序的团队编号。
输入样例
3 3
1 2 3000 0
1 2 3100 1
2 1 4200 1
输出样例
2 1 3
(1)编程思路。
定义结构体类型和结构体数组
struct Teams
{
int time;
int num;
int index;
int prob[21];
}team[1001];
来保存各参赛团队的情况。其中,成员分量index保存团队编号,num保存团队正确解答问题的数量,time保存团队的总用时,数组prob保存团队对各问题的解答情况,除团队编号外,其余成员变量的初始值全为0。
成员数组元素prob[i]=0表示问题i未提交过,若第i题提交了,但不是正确解答,则prob[i]加1;若问题i提交被AC,则修改团队的num和time,同时置prob[i]=-1,这表示问题i已被正确解决,以后若还有对问题i的提交,不管正确与否,均忽略不再进行处理,因为如果再更新time,只会更大,无意义。
定义结构体数组
struct Runs
{
int ci,pi,ti,ri;
} run[1001];
来保存提交情况,其中:ci表示团队编号,pi表示问题编号,ti表示提交时间(以秒为单位),ri值为1或0,如果提交结果为AC,则为1,否则为0。
输入了提交情况后,将run数组按提交时间从小到大进行排序,之后,遍历数组中的每个元素进行相应处理,并对应修改提交团队的相关成员分量。
各提交情况循环处理完后,将team数组按解答问题的正确数量num从大到小排列,若num相同,则按总用时time从小到大排列,若time还相同,则按团队编号从小到大排列。
(2)源程序。
#include <stdio.h> #include <string.h> struct Teams { int time; int num; int index; int prob[21]; }; struct Runs { int ci,pi,ti,ri; }; int cmp1(struct Runs a,struct Runs b) { if (a.ti!=b.ti) return a.ti>b.ti; else return a.ri>b.ri; } int cmp2(struct Teams c,struct Teams d) { if (c.num!=d.num) return d.num>c.num; else if (c.time!=d.time) return c.time>d.time; else return c.index>d.index; } int main() { struct Teams team[1001]; memset(team,0,sizeof(team)); int m,n; scanf("%d%d",&m,&n); int i,j; for(i=0;i<m;i++) { team[i].index=i; team[i].time=team[i].num=0; for (j=0;j<=20;j++) team[i].prob[j]=0; } struct Runs run[1001]; for (i=0;i<n;i++) { scanf("%d%d%d%d",&run[i].ci,&run[i].pi,&run[i].ti,&run[i].ri); } for (i=0;i<n-1;i++) for (j=0;j<n-1-i;j++) { if (cmp1(run[j],run[j+1])) { struct Runs temp1; temp1=run[j]; run[j]=run[j+1]; run[j+1]=temp1; } } for (i=0;i<n;i++) { if(run[i].ri==0) { if (team[run[i].ci-1].prob[run[i].pi]!=-1) team[run[i].ci-1].prob[run[i].pi]++; } else { if (team[run[i].ci-1].prob[run[i].pi]!=-1) { team[run[i].ci-1].time+=run[i].ti+1200*team[run[i].ci-1].prob[run[i].pi]; team[run[i].ci-1].prob[run[i].pi]=-1; team[run[i].ci-1].num++; } } } for (i=0;i<m-1;i++) for (j=0;j<m-1-i;j++) { if (cmp2(team[j],team[j+1])) { struct Teams temp2; temp2=team[j]; team[j]=team[j+1]; team[j+1]=temp2; } } for (i=0;i<m-1;i++) printf("%d ",team[i].index+1); printf("%d\n",team[m-1].index+1); return 0; }
将上面的源程序提交给北大POJ题库 POJ 2379 ACM Rank Table (http://poj.org/problem?id=2379),可以Accepted。
习题76
76-1 确定获胜者
问题描述
某次编程竞赛有4道赛题,有n只团队参加了这一竞赛。
在竞赛规则中,一个团队的得分有两个组成部分。首先是解决了多少问题。第二个是罚分,它反映了问题解决之前的时间和错误提交。对于每个正确解决的问题,罚分等于解决问题的时间加上每次错误提交的20分钟。对于没有正确解决的问题,不会增加罚分。
因此,如果一个团队在20分钟内第二次提交时解决了问题1,他们将被罚40分。如果他们提交问题2三次,但没有解决,他们将不被罚分。如果他们提交问题3一次,并在120分钟内解决,他们将被处以120分的罚分。他们的总分是两道题,共160分。
获胜者是解决最多问题的团队。如果团队在解决最多问题上打成平局,那么获胜者就是罚分最少的团队。
请编写程序根据给定的n个团队的提交结果,确定最终的获胜者。
输入
第1行是一个整数n,表示参赛团队数量;
之后n行,每行表示一个参赛团队的提交情况,格式为:
<Name> <p1Sub> <p1Time> <p2Sub> <p2Time>…<p4Time>
行中的第一个元素是团队名称,它不包含空格。接下来,对于四个问题中的每一个,是团队提交该问题的运行次数以及正确解决该问题的时间(两个整数)。如果一个团队没有解决该问题,时间将为零。如果问题得到解决,提交的数量将至少为1。
输入保证不会导致参赛团队之间的平局。
输出
输出由一行组成,列出获胜团队的名称、他们解决的问题数量以及他们的罚分。
输入样例
4
Stars 2 20 5 0 4 190 3 220
Rockets 5 180 1 0 2 0 3 100
Penguins 1 15 3 120 1 300 4 0
Marsupials 9 0 3 100 2 220 3 80
输出样例
Penguins 3 475
(1)编程思路。
定义结构体数组
struct Team
{
char name[20];
int s1,t1,s2,t2,s3,t3,s4,t4;
int total,time;
} teams[100];
来保存各参赛团队的解答情况。其中,name保存团队名称,s1,t1,s2,t2,s3,t3,s4,t4分别保存团队对4道赛题所提交的运行次数以及正确解决该问题的时间,total保存解决的问题数量,time保存团队的总罚分。
(2)源程序。
#include <stdio.h> #include <string.h> struct Team { char name[20]; int s1,t1,s2,t2,s3,t3,s4,t4; int total,time; }; int main() { struct Team teams[100]; int i,n,win,max,cnt; scanf("%d",&n); max=-1; for(i=0;i<n;i++) { teams[i].time=0; scanf("%s%d%d%d%d%d%d%d%d",teams[i].name,&teams[i].s1 ,&teams[i].t1,&teams[i].s2,&teams[i].t2 ,&teams[i].s3,&teams[i].t3,&teams[i].s4,&teams[i].t4); cnt=0; if (teams[i].t1) { cnt++; teams[i].time+=(teams[i].s1-1)*20+teams[i].t1; } if (teams[i].t2) { cnt++; teams[i].time+=(teams[i].s2-1)*20+teams[i].t2; } if(teams[i].t3) { cnt++; teams[i].time+=(teams[i].s3-1)*20+teams[i].t3; } if(teams[i].t4) { cnt++; teams[i].time+=(teams[i].s4-1)*20+teams[i].t4; } teams[i].total=cnt; if (cnt>max) { max=cnt; win=i; } else if(cnt==max) { if(teams[i].time<teams[win].time) win=i; } } printf("%s %d %d\n",teams[win].name,teams[win].total,teams[win].time); return 0; }
76-2 排名表
问题描述
编写一个程序,根据给定的一份参与团队列表和一份描述参赛团队提交的所有解决方案的日志文件,确定编程竞赛的排名列表。
本次编程竞赛的评分基于以下规则:
参赛团队将根据正确解决赛题的总题数进行排名,解决相同数量问题的团队按最少总时间排名。总时间是解决每个问题所花费的时间之和。解决问题所消耗的时间是从比赛开始到提交合格运行记录所用的时间,再加上该问题每次被错误运行的20分钟罚时(无论提交时间如何),没有解决的问题也就没有耗时。另外,在同一团队提交了正确的解决方案后,之后提交错误的解决方案将不会受到罚时。
如果两个或两个以上的团队有相同的正确答题总数和相同的总时间,则为这些团队分配相同的排名,并按团队名称字母顺序列出。例如,如果两个最好的团队解决问题的数量和总时间一致,他们都将获得排名1,而下一个团队将被分配到排名3。排名2在排名表中不会出现。
输入
输入包含多组测试用例,第1行为测试用例的组数。
对于每个测试用例,在第1行中给出团队的数量n(1<=n<=20),在接下来的n行中给出团队的(唯一)名称。团队名称是一个长度不超过8的单词,仅包含字母和数字,为了方便起见,团队名称按字母顺序列出。下一行包含问题的数量k和提交的解决方案的数量m(1<=k<10,0<=m<=2000)。以下m行中的每一行都以“problem time correctness team”的形式描述了一个这样的解决方案,其中problem是问题的编号(1 <= problem <= k),time是比赛开始后经过的分钟数(0 <= time < 300),correctness是“Yes”或“No”,team是提交解决方案的团队的名称。可以假设日志文件中的行是按时间排序的。
输出
对于每个测试用例,输出一份包含参加比赛的每个团队的排名列表。格式是“rank.team solved time”,其中rank是排名,team是团队名称,solved是正确解题的数量,time是总时间。除了分隔这四个字段的单个空格外,还可以使用字段宽度2表示排名,8表示团队名称,1表示已解决问题的数量,4表示总时间(名称左对齐,数字右对齐,请参见输出样例),使表格看起来更漂亮。每个测试用例都以一个空行结束。
输入样例
2
10
Team1
Team2
Team3
Team4
Team5
Team6
Team7
Team8
Team9
slowTeam
8 14
1 18 Yes Team4
1 57 Yes Team2
1 87 Yes Team3
1 101 Yes Team1
2 103 Yes Team5
2 120 Yes Team6
6 141 Yes Team7
1 147 No Team1
7 156 Yes Team2
5 167 Yes Team8
2 167 Yes Team9
5 170 No Team4
5 175 Yes Team4
1 234 No slowTeam
1
Team1
8 0
输出样例
1. Team2 2 213
1. Team4 2 213
3. Team3 1 87
4. Team1 1 101
5. Team5 1 103
6. Team6 1 120
7. Team7 1 141
8. Team8 1 167
8. Team9 1 167
10. slowTeam 0 0
1. Team1 0 0
(1)编程思路。
参照例76的编程思路进行处理,但由于输入的提交情况已按提交时间排序,因此无需对输入的提交情况进行排序,这样也就不需要用结构体数组来保存各提交情况,每输入一个提交情况,就直接进行处理。
(2)源程序。
#include <stdio.h> #include <string.h> struct Teams { char name[10]; int index; int time; int num; int prob[11]; }; int cmp(struct Teams a,struct Teams b) { if (a.num!=b.num) return b.num>a.num; else if (a.time!=b.time) return a.time>b.time; else return a.index>b.index; } int main() { struct Teams team[21]; memset(team,0,sizeof(team)); int t; scanf("%d",&t); while (t--) { int n; scanf("%d",&n); int i,j; for (i=0;i<n;i++) { scanf("%s",team[i].name); team[i].time=0; team[i].num=0; team[i].index=i+1; for (j=0;j<10;j++) team[i].prob[j]=0; } int k,m; scanf("%d%d",&k,&m); for (i=0;i<m;i++) { int pno,ts; char corr[5],name[10]; scanf("%d%d%s%s",&pno,&ts,corr,name); int id; for (id=0;id<n;id++) if (strcmp(team[id].name,name)==0) break; if (corr[0]=='N') { if (team[id].prob[pno]!=-1) team[id].prob[pno]++; } else { if (team[id].prob[pno]!=-1) { team[id].time+=(ts+20*team[id].prob[pno]); team[id].prob[pno]=-1; team[id].num++; } } } for (i=0;i<n-1;i++) for (j=0;j<n-1-i;j++) { if (cmp(team[j],team[j+1])) { struct Teams temp; temp=team[j]; team[j]=team[j+1]; team[j+1]=temp; } } int rank=0; for (i=0;i<n;i++) { if (i==0 || team[i].num!=team[i-1].num || team[i].time!=team[i-1].time) rank=i+1; printf("%2d. %-8s %d %4d\n",rank,team[i].name,team[i].num,team[i].time); } printf("\n"); } return 0; }
将上面的源程序提交给北大POJ题库POJ 1918 Ranking List (http://poj.org/problem?id=1918) ,可以Accepted。
76-3 竞赛排名
问题描述
某次程序设计竞赛有7道题目,有C只团队参加竞赛。
团队排名顺序为:首先是解决的问题最多,其次是总时间最少,然后是所有非零时间的最小几何平均数。仍处于平局的团队将获得相同的数字排名,并按团队名称区分大小写的字符串比较按字母顺序列出。
对于这个问题,所有几何平均值都将被四舍五入为一个整数,如下所述,在计算排名和显示结果时,只使用四舍五入值。
如果所有时间均为零,则几何平均值也为零。否则,如果有n个非零乘以t1,…,tn,则几何平均值定义为 exp((ln t1+ ln t2+…+ln tn)/n)
其中exp x表示ex,ln x表示x的自然对数。计算几何平均值后,通过加0.5并截断任何小数位数将其四舍五入为整数。
编写程序,对这个编程竞赛进行排名。
输入
输入文件包含一个或多个竞赛,后面有一行仅包含零,表示文件结束。
每场比赛以一行开头,其中包含一个不大于20的正整数c,表示比赛中的团队数量,然后是c行,其中包含团队名称和七个问题的解决时间,用空格分隔。团队名称由1到10个字母组成。比赛中的所有团队名称都是唯一的。时间是不大于500的非负整数。
输出
对于每场比赛,必须用表格形式输出排名。所有表格的宽度相同,长度等于参赛队伍的数量。使用示例中所示的精确格式。每场比赛都有一个编号的标题,后面是一个表格,每行有一个团队条目。每个条目都包含排名、团队名称、解决的问题、总时间、几何平均数,然后是与输入中出现的顺序相同的单个解决时间。团队名称左对齐,所有其他字段右对齐。排名总是有两位数,必要时包括前导零。空格永远不会出现在行首或行尾。
输入样例
1
Plutonians 123 234 345 456 167 278 389
4
Xap 0 0 0 0 0 0 0
Foo 20 30 0 50 40 0 10
Bar 0 50 20 0 10 40 30
Baz 0 0 0 0 0 0 0
3
Venus 213 0 0 57 0 0 0
Neptune 0 0 0 117 153 0 0
Mars 0 150 0 0 0 0 120
0
输出样例
(1)编程思路。
定义结构体数组
struct Team
{
char name[12];
int sol, tot, g, p[7], ind;
} teams[25];
来保存各团队的参赛情况。其中,name保存团队名称,ind保存团队的最后名次,sol保存团队解决问题的数量,tot保存团队解决问题的总时间,g保存几何平均数,数组p[7]保存7个问题的单个解决时间。
(2)源程序。
// POJ 1245 Programmer, Rank Thyself (http://poj.org/problem?id=1245)
#include <stdio.h> #include <string.h> #include <math.h> struct Team { char name[12]; int sol, tot, g, p[7], ind; }; struct Team teams[25]; int cmp(struct Team a, struct Team b) { if (a.sol!=b.sol) return b.sol>a.sol; else if (a.tot!=b.tot) return a.tot > b.tot; else if (a.g!=b.g) return a.g > b.g; else return strcmp(a.name, b.name)>=0; } int main() { int n=0,cnt=1,i,j; while(scanf("%d", &n) && n!=0) { for (i=0; i<n;i++) { double t = 0; teams[i].sol = 0, teams[i].tot = 0, teams[i].g = 0; scanf("%s", teams[i].name); for(j=0; j<7; j++) { scanf("%d", &teams[i].p[j]); if(teams[i].p[j]) { teams[i].sol++; teams[i].tot += teams[i].p[j]; t += log((double)teams[i].p[j]); } } if(teams[i].sol > 0) teams[i].g = exp(t/teams[i].sol) + 0.5; } for (i=0;i<n-1;i++) for (j=0;j<n-1-i;j++) { if (cmp(teams[j],teams[j+1])) { struct Team temp; temp=teams[j]; teams[j]=teams[j+1]; teams[j+1]=temp; } } printf("CONTEST %d\n", cnt); cnt++; for(i=0; i<n; i++) { if(i < 9) printf("0"); if(i > 0 && teams[i].sol == teams[i-1].sol && teams[i].tot == teams[i-1].tot && teams[i].g == teams[i-1].g) teams[i].ind = teams[i-1].ind; else teams[i].ind = i + 1; printf("%d %-10s %d %4d %3d %3d %3d %3d %3d %3d %3d %3d\n", teams[i].ind, teams[i].name, teams[i].sol, teams[i].tot, teams[i].g, teams[i].p[0], teams[i].p[1], teams[i].p[2], teams[i].p[3], teams[i].p[4], teams[i].p[5], teams[i].p[6]); } } return 0; }