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

这道题参考了刘汝佳老师的代码思路,真的是将C++运用到了极致。

题目大意

有n(n <= 144) 名选手,输入的信息为姓名,四轮比赛的得分;输入总奖金池,并给出前70名的奖金比例;输出所有紧急之后的排名,并给出排名信息及应得奖金。晋级名额通过前两轮的总得分来划分,越少越好,如果那一轮比赛选手被淘汰会输入 DQ ,并且选手分为职业和业余选手。排名规则:所有选手均参与排名,但是业余选手不参与奖金分配,奖金分配中是没有并列的情况的;如果有选手并列要区分是否是专业选手,然后通过四轮总得分排名,对于晋级之后淘汰的全部放在最后先根据回合数进行排名,然后根据总得分排名最后根据名字排名。但是总成绩输出为 DQ。

解题思路

  1. 输入数据有所难度,全文采用gets() 方法来读取每一行,但是由于gets()方法会出现栈溢出问题,所以在C++ 11中会被标记为弃用,但是有的编译器还是会运行。建议换成fgets(s,sizeof(s),stdin);用法是一样的。
  2. 通过gets读取完的每一行,都可以通过sscanf(s,“%d”,&T);输入到目标变量中,它的返回值有两种:如果没有找到目标变量返回0;如果遇到错误返回-1;其他情况就返回读入几个值;这在本题中应用判断是否存在被淘汰的情况。
  3. 那么这一题需要排序两次,并且每次的排序规则都不同,通过sort中的自定义规则来排序
  4. 最后就是输出,主要判断输出的格式即可,分为业余、非业余和淘汰选手,本题解的输出方式就是循环遍历每一个选手的情况,判断为哪一类,然后通过while记录有多少个并列,最后计算奖金分红。

AC代码

/*
	1、确定选手属性,姓名、四轮成绩、是否是业余
	有些选手会出现在比赛中取消比赛资格。
	2、要先选定晋级的选手,根据前两轮的得分较低的前70名,
	可以是并列的。
	3、根据晋级选手的四轮的比赛得出最终排名,再根据奖金
	池的比例分配奖金。
	4、关于奖金分配,如果有并列,就将这两名总共的奖金平分
	奖金是没有并列的,依次分发。
	5、业余选手不得参加选手排名,名称后面标 *
	6、输出列出所有晋级36洞比赛的选手
	7、注意奖金比例只会分配给前70名选手
	8、某轮比赛资格被取消,乘积为DQ
	9、对于非业余选手,出现并列的情况,排名后加上T,业余
	选手不管。
	10、对于平局选手的排名:
		1. 按照回合数和总计数进行排名
		2. 按照字母顺序排名
*/

#include <bits/stdc++.h>

using namespace std;

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

const int N_BONUS = 70;
double total_bonus;
double bonus[100];
int player_num;

// 定义选手类
struct Player {
	char name[30];
	int RD[4];
	int amateur; 	// 表示业余选手
	int rd36,rd72;	// 前两轮和总共的得分
	int times,dq;	// 回合数 和 是否要淘汰
} players[150];

// 晋级排名
bool cmp(const Player& a,const Player& b) {
	// 将要淘汰掉的人都向后排
	if(a.rd36 < 0 && b.rd36 < 0) return false;
	if(a.rd36 < 0) return false;
	if(b.rd36 < 0) return true;
	// 从小到大排
	return a.rd36 < b.rd36;
}

bool cmp2(const Player& a,const Player& b) {
	// 排序晋级后的选手
	// 几要素:回合数、淘汰、总计数、人名
	if(a.dq && b.dq) {
		// 都是要淘汰掉的
		if(a.times != b.times) return a.times > b.times;
		if(a.rd72 != b.rd72) return a.rd72 < b.rd72;
		return strcmp(a.name,b.name) < 0;
	}
	// 要淘汰的都向后靠
	if(a.dq) return false;
	if(b.dq) return true;
	if(a.rd72 != b.rd72) return a.rd72 < b.rd72;
	return strcmp(a.name,b.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;	// pos 存储奖金分级
	while(i < player_num) {
		if(players[i].dq) {
			// 输出被取消资格的选手
			printf("%s           ",players[i].name);
			FOR(k,players[i].times) printf("%-5d",players[i].RD[k]);
			FOR(k,4-players[i].times) printf("     ");
			printf("DQ\n");
			i++;
			continue;
		}

		int j = i;
		int m = 0; // 记录多少个并列的专业选手
		bool have_money = false;
		double tot = 0.0;
		while(j < player_num && players[i].rd72 == players[j].rd72) {
			if(!players[j].amateur) {
				// 专业选手
				m++;
				if(pos < N_BONUS) {
					// 奖金还有得分
					have_money = true;
					tot += bonus[pos++];
				}
			}
			j++;
		}

		int rank = i+1;
		double amount = total_bonus * tot / m;
		while(i < j) {
			printf("%s ",players[i].name);
			char t[5];
			sprintf(t,"%d%c",rank,m > 1 && have_money && !players[i].amateur ? 'T':' ');
			printf("%-10s",t);
			FOR(e,4) {
				printf("%-5d",players[i].RD[e]);
			}

			if(!players[i].amateur && have_money) {
				printf("%-10d",players[i].rd72);
				printf("$%9.2lf\n",amount / 100.0);
			} else {
				printf("%d\n",players[i].rd72);
			}
			i++;
		}
	}
}

int main() {
	int T;
	char s[40];
	gets(s,sizeof(s),stdin);
	sscanf(s,"%d",&T);
	while(T--) {
		gets(s,sizeof(s),stdin); // 空行

		// 奖金分配
		gets(s,sizeof(s),stdin);
		sscanf(s,"%lf",&total_bonus);
		FOR(i,N_BONUS) {
			gets(s,sizeof(s),stdin);
			sscanf(s,"%lf",&bonus[i]);
		}

		// 参赛选手

		gets(s,sizeof(s),stdin);
		sscanf(s,"%d",&player_num);
		assert(player_num <= 144);
		FOR(i,player_num) {
			gets(s,sizeof(s),stdin);
			strncpy(players[i].name,s,20);
			players[i].name[20] = 0;
			players[i].amateur = 0;
			if(strchr(players[i].name,'*')) {
				players[i].amateur = 1;
			}

			players[i].rd36 = players[i].rd72 = players[i].dq = 0;
			memset(players[i].RD,-1,sizeof(players[i].RD));
			FOR(j,4) {

				// 将分数单独存起来
				char t[5];
				FOR(k,3) {
					t[k] = s[20 + j*3 + k];
					t[3] = '\0';
				}

				if(!sscanf(t,"%d",&players[i].RD[j])) {
					// 说明遇到要淘汰的了
					// 记录下回合数
					players[i].times = j;
					players[i].dq = -1;	 // 标志下淘汰了
					if(j < 2) {
						players[i].rd36 = -1;
					}
					break;
				} else {
					players[i].rd72 += players[i].RD[j];
					if(j < 2) {
						players[i].rd36 += players[i].RD[j];
					}
				}
			}
		}

		// 第一次排序
		sort(players,players+player_num,cmp);
		assert(players[N_BONUS-1].rd36 >= 0);	// 打断点
		// 更新选手数量
		for(int i = N_BONUS-1; i < player_num; i++) {
			if(i == player_num-1 || players[i].rd36 != players[i+1].rd36) {
				player_num = i+1;
				break;
			}
		}
		sort(players,players+player_num,cmp2);

//		FOR(i,player_num) {
//			cout<<players[i].name<<"  "<<players[i].rd72<<endl;
//		}

		// 打印输出
		print_result();

		if(T) printf("\n");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值