5.4 例题 5-10 PGA巡回赛的奖金

题目链接:https://vjudge.net/problem/UVA-207

题目描述:(简单描述)

一场高尔夫比赛,分四轮,每个选手都打完前两轮,前70名(包括并列)晋级,再打两轮,最后四轮总分前70名(包括并列)都有奖金。成绩是越少越好。

要求:给出总奖金和每个名次获奖比例(前70名),还有所有选手的名字和每轮成绩

输出所有晋级到后两轮的选手的信息,从第一名开始,并列选手按名字字典做升序输出,输出选手的名字,排名,各轮得分,总分,奖金数。

注意:

1、选手分为职业和业余,业余名字结尾带*,可晋级,参与排名,但最后没有奖金。

     若业余选手得到了第三名,则第四名(非业余)拿第三名的奖金比例,以此类推

2、选手每轮都有可能犯规,即该轮成绩处记为DQ,则后面轮次没有成绩。

     若为晋级选手,输出时排在最后,没有名次,总分记为DQ。

     若有犯规选手并列,则先按轮数排序,然后按各轮得分之和排序,最后按名字排序

3、最后如果第k名有n名选手并列,则k~k+n-1名的奖金比例相加后平均分给这n人。

     输出时并列选手名次后加一个标记T。

4、奖金四舍五入到美分。如果没取消资格(没犯规)的非业余选手小于70名,剩下的奖金就不发了。

      只要选手在前70名,奖金为0也要输出。

5、输入时,只要选手没犯规,就会给出四轮成绩

   (就算没晋级也会有,但在实际比赛中没晋级的选手应该只有两个成绩)

整体分析

    第一步是选出晋级选手,先对前两轮得分进行排序。接下来计算4轮总分,然后再排序一次,最后对排序结果依次输出。

题目输入格式:(原题是英文,这里是用谷歌翻译,有些地方会有出入,看得懂英文的建议点上面链接)

题目输出格式:(同上)


大致流程图:

代码:(这是GitHub上的紫书代码,加我的一些注释)

(用vs2019编译器,原码中的gets(s)不让编译,所以我都换成了cin.getline(s,40),不影响结果)

原码链接:https://github.com/aoapc-book/aoapc-bac2nd/blob/master/ch5/UVa207.cpp

//UVa207 PGA Tour Prize Money

//Rujia Liu

#include<cstdio>

#include<cstdlib>

#include<cstring>

#include<cmath>

#include<algorithm>

#include<cassert> 


using namespace std;

#define REP(i,n) for(int i = 0; i < (n); i++)

const int maxn = 144;

const int n_cut = 70;

struct Player {

char name[25];

int amateur; //标记是否为业余选手

int sc[4]; //记录每个成绩

int sc36, //记录前两轮总分

sc72, //记录总分

dq; //标记是否犯规

int rnds; //若犯规,记录已完成的场次

} player[maxn];

int n;

double purse, p[n_cut];

bool cmp1(const Player& p1, const Player& p2) {

if (p1.sc36 < 0 && p2.sc36 < 0) return false; // equal sc36<0说明为犯规选手,无法晋级,排最后

if (p1.sc36 < 0) return false; // p2 smaller

if (p2.sc36 < 0) return true; // p1 smaller

return p1.sc36 < p2.sc36;

}

bool cmp2(const Player& p1, const Player& p2) {

if (p1.dq && p2.dq) { /*如果都是DQ选手,场次多的->总分小的->名字字典小的*/

if (p1.rnds != p2.rnds) return p2.rnds < p1.rnds;

if (p1.sc72 != p2.sc72) return p1.sc72 < p2.sc72;

return strcmp(p1.name, p2.name) < 0;

}

if (p1.dq) return false; //是否QD选手->总分小的->名字字典小的

if (p2.dq) return true;

if (p1.sc72 != p2.sc72) return p1.sc72 < p2.sc72;

return strcmp(p1.name, p2.name) < 0;

}

void print_result() {

printf("Player Name          Place    RD1  RD2");

printf("  RD3  RD4  TOTAL    Money Won\n");

printf("---------------------------------------");

printf("--------------------------------\n");

int i = 0, pos = 0;

while (i < n) {

if (player[i].dq) //如果为DQ选手,只输出已完成的成绩,未完成的输出空格,总分记为DQ

{

printf("%s          ", player[i].name);

REP(j, player[i].rnds) printf("%-5d", player[i].sc[j]);

REP(j, 4 - player[i].rnds) printf("    ");

printf("DQ\n");

i++;

continue;

}

int j = i;

int m = 0; // number of tied players 并列人数(可以分奖金的,业余不算)

bool have_money = false;

double tot = 0.0; // total pooled money

while (j < n && player[i].sc72 == player[j].sc72) { /*1、检测选手本身是否为业余选手

2、检测是否有并列,并列选手是否为业余选手*/

if (!player[j].amateur) {

m++;

if (pos < n_cut) {

have_money = true; // yeah! they still have money

tot += p[pos++];

}

}

j++;

}

// print player [i,j) together because they have the same rank

int rank = i + 1; // rank of all these m players 名次(因为i从0开始,要加1)

double amount = purse * tot / m; // if m=0, amount will be nan but we don't use it in that case :)

while (i < j) {

printf("%s ", player[i].name);

char t[5];

sprintf(t, "%d%c", rank, m > 1 && have_money && !player[i].amateur ? 'T' : ' '); //输出名次,同时检测是否有并列,有就加T

printf("%-10s", t);

REP(e, 4) printf("%-5d", player[i].sc[e]);

// with prize

if (!player[i].amateur && have_money) { //检测是否可以有奖金。(不是业余)

printf("%-10d", player[i].sc72);

printf("$%9.2lf\n", amount / 100.0);

}

else

printf("%d\n", player[i].sc72);

i++;

}

}

}

int main() {

int T;

char s[40];

cin.getline(s,40);

sscanf(s, "%d", &T);

while (T--) {

cin.getline(s, 40); // empty line

// prize

cin.getline(s, 40);

sscanf(s, "%lf", &purse);

REP(i, n_cut) {

cin.getline(s, 40);

sscanf(s, "%lf", &p[i]); //记录总奖金和每个名次所得比例

}

// players

cin.getline(s, 40);

sscanf(s, "%d", &n);

assert(n <= 144);

REP(k, n) {

// read a 32-character line

cin.getline(s, 40);

// player name

strncpy(player[k].name, s, 20); //选手名字,小于20个字符

player[k].name[20] = 0;

player[k].amateur = 0;

if (strchr(player[k].name, '*')) { //如果有‘*’,标记为业余(amateur=1)

player[k].amateur = 1;

}

// scores

player[k].sc36 = player[k].sc72 = player[k].dq = 0;

memset(player[k].sc, -1, sizeof(player[k].sc));

REP(i, 4) {

// raw score

char t[5];

REP(j, 3) t[j] = s[20 + i * 3 + j]; t[3] = '\0'; /*前面20个字符为名字,后面四个成绩,随时会有DQ,

用t[5]依次存储每个成绩,t[0]为' ',t[1],t[2]为成绩,t[3]为'\0'代表结束*/

// parse

if (!sscanf(t, "%d", &player[k].sc[i])) { /*当t[]存储不为DQ,会返回1,结果为false,运行else,记录成绩

当t[]存储为DQ,会返回0,结果为ture,运行if

标记为DQ选手(dq=-1),记录犯规的场次,如果前两场没犯规,也记录成绩,

最后要break,因为犯规后不能再参加比赛*/

// DQ!

player[k].rnds = i;

player[k].dq = -1;

if (i < 2) player[k].sc36 = -1; //前两轮有犯规,说明已经晋级不了,标记为-1

break; // skip other rounds (filled with -1, initially) //其他场次成绩已经用-1填充,前面的memset()

}

else {

player[k].sc72 += player[k].sc[i];

if (i < 2)

player[k].sc36 += player[k].sc[i];

}

}

}

// round 1

sort(player, player + n, cmp1);

assert(player[n_cut - 1].sc36 >= 0);

/*检测是否有更多选手晋级

用第70名选手前两轮分数和后一位比较

如果相同,晋级选手加一,直到有不同分数的选手(已排序,不同则说明分数更差,之后就不能晋级了)*/

for (int i = n_cut - 1; i < n; i++)

if (i == n - 1 || player[i].sc36 != player[i + 1].sc36) { n = i + 1; break; }

// round 2

sort(player, player + n, cmp2);

// print result

print_result();

if (T) printf("\n");

}

return 0;

}

void main4()

{

int i, j;

int a[10][10] = { 0 };

for (i = 1; i < 10; i++)

{

a[0][0] = 1;

a[i][0] = 1;

a[i][i] = 1;

for (j = 1; j < i; j++)

a[i][j] = a[i - 1][j - 1] + a[i - 1][j];

}

for (i = 0; i < 10; i++)

{

for (j = 0; j <= i; j++)

printf("%d ", a[i][j]);

printf("\n");

}

}


有个博客里有输入输出案例(题目没有完整给),可以自己测试https://blog.csdn.net/crazysillynerd/article/details/43763003


学到到的一些函数:

1、sscanf(s,"%d",&T):sscanf将s中的字符串以整数的形式赋给T,这里是赋予比赛数量

     代码中多次用到cin.getline(s, 40);sscanf(s, "%lf", &p[i]);

    可能会奇怪,为什么不直接用cin>>p[i];

    这是因为有时候会从缓冲区里读到空格,导致整个程序出错

       有些地方可以换,没影响,但可能是为了整体风格一致,所以统一用了sscanf。

2、sprintf(t, "%d%c", rank, m > 1 && have_money && !player[i].amateur ? 'T' : ' '):

      sprintf的作用是将格式化的字符串输出到一个目的字符串中,这里是先以整数赋予名次rank,再判断该名次是否有并列选手,如果有就赋予字符T。之后输出t,就代表真正的名次了。

3、assert(n <= 144):assert是一条检查错误的语句,比如题目说不会超过144名选手。那么,当超过时,即n>144,这条语句就会终止程序,并给出错误信息(错在哪一行)

如输入n=150,执行到这里会输出Assertion failed: n <= 144,file (文件路径),line (错误行数)

不过题目一般说明不会超过144,那么系统测试数据就不会主动超过(除非人为输入),就是说这条语句不写应该也可以ac的,所以我不是很明白写这条语句的意义。

4、宏定义#define REP(i,n) for(int i = 0; i < (n); i++):这算是一个技巧,当代码中多次要用到for循环时,一个REP(i,n)再改一下参数就可以搞定。不过这是我第一次遇到,理解代码时反而会有些不适应。不过,用到多次而相似的代码时,确实可以用这个技巧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335