5.4 例題 5-10 PGA巡回賽的獎金

題目鏈接:https://vjudge.net/problem/UVA-207

題目描述:(簡單描述)

一場高爾夫比賽,分四輪,每個選手都打完前兩輪,前70名(包括并列)晉級,再打兩輪,最后四輪總分前70名(包括并列)都有獎金。成績是越少越好。

要求:給出總獎金和每個名次獲獎比例(前70名),還有所有選手的名字和每輪成績

輸出所有晉級到后兩輪的選手的信息,從第一名開始,并列選手按名字字典做升序輸出,輸出選手的名字,排名,各輪得分,總分,獎金數(shù)。

注意:

1、選手分為職業(yè)和業(yè)余,業(yè)余名字結(jié)尾帶*,可晉級,參與排名,但最后沒有獎金。

?????若業(yè)余選手得到了第三名,則第四名(非業(yè)余)拿第三名的獎金比例,以此類推

2、選手每輪都有可能犯規(guī),即該輪成績處記為DQ,則后面輪次沒有成績。

? ? ?若為晉級選手,輸出時排在最后,沒有名次,總分記為DQ。

? ? ?若有犯規(guī)選手并列,則先按輪數(shù)排序,然后按各輪得分之和排序,最后按名字排序

3、最后如果第k名有n名選手并列,則k~k+n-1名的獎金比例相加后平均分給這n人。

? ? ?輸出時并列選手名次后加一個標(biāo)記T。

4、獎金四舍五入到美分。如果沒取消資格(沒犯規(guī))的非業(yè)余選手小于70名,剩下的獎金就不發(fā)了。

? ? ? 只要選手在前70名,獎金為0也要輸出。

5、輸入時,只要選手沒犯規(guī),就會給出四輪成績

? ?(就算沒晉級也會有,但在實際比賽中沒晉級的選手應(yīng)該只有兩個成績)

整體分析

? ? 第一步是選出晉級選手,先對前兩輪得分進行排序。接下來計算4輪總分,然后再排序一次,最后對排序結(jié)果依次輸出。

題目輸入格式:(原題是英文,這里是用谷歌翻譯,有些地方會有出入,看得懂英文的建議點上面鏈接)

題目輸出格式:(同上)


大致流程圖:

代碼:(這是GitHub上的紫書代碼,加我的一些注釋)

(用vs2019編譯器,原碼中的gets(s)不讓編譯,所以我都換成了cin.getline(s,40),不影響結(jié)果)

原碼鏈接: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; //標(biāo)記是否為業(yè)余選手

int sc[4]; //記錄每個成績

int sc36, //記錄前兩輪總分

sc72, //記錄總分

dq; //標(biāo)記是否犯規(guī)

int rnds; //若犯規(guī),記錄已完成的場次

} 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說明為犯規(guī)選手,無法晉級,排最后

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 并列人數(shù)(可以分獎金的,業(yè)余不算)

bool have_money = false;

double tot = 0.0; // total pooled money

while (j < n && player[i].sc72 == player[j].sc72) { /*1、檢測選手本身是否為業(yè)余選手

2、檢測是否有并列,并列選手是否為業(yè)余選手*/

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) { //檢測是否可以有獎金。(不是業(yè)余)

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, '*')) { //如果有‘*’,標(biāo)記為業(yè)余(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'代表結(jié)束*/

// parse

if (!sscanf(t, "%d", &player[k].sc[i])) { /*當(dāng)t[]存儲不為DQ,會返回1,結(jié)果為false,運行else,記錄成績

當(dāng)t[]存儲為DQ,會返回0,結(jié)果為ture,運行if

標(biāo)記為DQ選手(dq=-1),記錄犯規(guī)的場次,如果前兩場沒犯規(guī),也記錄成績,

最后要break,因為犯規(guī)后不能再參加比賽*/

// DQ!

player[k].rnds = i;

player[k].dq = -1;

if (i < 2) player[k].sc36 = -1; //前兩輪有犯規(guī),說明已經(jīng)晉級不了,標(biāo)記為-1

break; // skip other rounds (filled with -1, initially) //其他場次成績已經(jīng)用-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名選手前兩輪分?jǐn)?shù)和后一位比較

如果相同,晉級選手加一,直到有不同分?jǐn)?shù)的選手(已排序,不同則說明分?jǐn)?shù)更差,之后就不能晉級了)*/

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


學(xué)到到的一些函數(shù):

1、sscanf(s,"%d",&T):sscanf將s中的字符串以整數(shù)的形式賦給T,這里是賦予比賽數(shù)量

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

????可能會奇怪,為什么不直接用cin>>p[i];

? ? 這是因為有時候會從緩沖區(qū)里讀到空格,導(dǎo)致整個程序出錯

? ? ? ?有些地方可以換,沒影響,但可能是為了整體風(fēng)格一致,所以統(tǒng)一用了sscanf。

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

? ? ? sprintf的作用是將格式化的字符串輸出到一個目的字符串中,這里是先以整數(shù)賦予名次rank,再判斷該名次是否有并列選手,如果有就賦予字符T。之后輸出t,就代表真正的名次了。

3、assert(n <= 144):assert是一條檢查錯誤的語句,比如題目說不會超過144名選手。那么,當(dāng)超過時,即n>144,這條語句就會終止程序,并給出錯誤信息(錯在哪一行)

如輸入n=150,執(zhí)行到這里會輸出Assertion failed: n <= 144,file (文件路徑),line (錯誤行數(shù))

不過題目一般說明不會超過144,那么系統(tǒng)測試數(shù)據(jù)就不會主動超過(除非人為輸入),就是說這條語句不寫應(yīng)該也可以ac的,所以我不是很明白寫這條語句的意義。

4、宏定義#define REP(i,n) for(int i = 0; i < (n); i++):這算是一個技巧,當(dāng)代碼中多次要用到for循環(huán)時,一個REP(i,n)再改一下參數(shù)就可以搞定。不過這是我第一次遇到,理解代碼時反而會有些不適應(yīng)。不過,用到多次而相似的代碼時,確實可以用這個技巧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,621評論 2 380