这道题参考了刘汝佳老师的代码思路,真的是将C++运用到了极致。
题目大意
有n(n <= 144) 名选手,输入的信息为姓名,四轮比赛的得分;输入总奖金池,并给出前70名的奖金比例;输出所有紧急之后的排名,并给出排名信息及应得奖金。晋级名额通过前两轮的总得分来划分,越少越好,如果那一轮比赛选手被淘汰会输入 DQ ,并且选手分为职业和业余选手。排名规则:所有选手均参与排名,但是业余选手不参与奖金分配,奖金分配中是没有并列的情况的;如果有选手并列要区分是否是专业选手,然后通过四轮总得分排名,对于晋级之后淘汰的全部放在最后先根据回合数进行排名,然后根据总得分排名最后根据名字排名。但是总成绩输出为 DQ。
解题思路
- 输入数据有所难度,全文采用gets() 方法来读取每一行,但是由于gets()方法会出现栈溢出问题,所以在C++ 11中会被标记为弃用,但是有的编译器还是会运行。建议换成fgets(s,sizeof(s),stdin);用法是一样的。
- 通过gets读取完的每一行,都可以通过sscanf(s,“%d”,&T);输入到目标变量中,它的返回值有两种:如果没有找到目标变量返回0;如果遇到错误返回-1;其他情况就返回读入几个值;这在本题中应用判断是否存在被淘汰的情况。
- 那么这一题需要排序两次,并且每次的排序规则都不同,通过sort中的自定义规则来排序
- 最后就是输出,主要判断输出的格式即可,分为业余、非业余和淘汰选手,本题解的输出方式就是循环遍历每一个选手的情况,判断为哪一类,然后通过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;
}