UVa207
根据专业高尔夫联赛(Professional Golf Association,PGA)的排名规则对选手进行排名:
- 每个选手至少打两轮比赛,除非在前两轮中被取消了资格
- 如果在某一轮中被取消了资格,则终止后面的比赛,在前两轮中被取消资格的选手将不能晋级。如果在晋级后两轮中被取消了资格则不能获得奖金
- 前两轮结束后,将有70名选手晋级,如果出现并列的情况,也可以有多于70名选手晋级
- 4轮全部打完的选手将根据4轮的总分数进行排名,总分数越低排名越高
- 排名后选手获得的奖金由该名次对应的百分比确定
- 输入数据保证只有一个第一名
- 如果在后续名次(第2到第70名)中出现了并列的情况,则名次并列选手所获奖金的百分比为并列名次对应的百分比的均值
- 如果可以获得奖金的选手少于70人,则剩余的奖金将不发放
- 业余选手不获得奖金,即业余选手名次所对应的百分比顺延到下一位专业选手
- 排名前70以及与晋级的最后一名选手并列的选手可以获得奖金
只输出晋级后的选手,排名从高到低,在后两轮中被取消资格的选手在最后输出,按照其所打的轮次、总分和姓名字典序的优先级进行排名。
这道题没有输入输出样例,同时uDebug上的测试用例覆盖也不够全面,因此调试起来非常困难。
首先确定可以晋级的选手,分四步完成:
- 根据选手是否完成了前两轮比赛来对选手们进行划分(
stable_partition
和DisqualifyPred
) - 计算完成了前两轮的选手的总分(
for_each
和AggregateScorePred
) - 根据总分从低到高进行排序(
sort
和SameRoundPartitionPred
) - 选出前70名和并列最后一名的选手,这就是需要输出的所有选手
其次要对晋级的选手进行排序。由于只有完成4轮比赛才有可能获得奖金,因此可以先根据是否完成了4轮比赛来对晋级后的选手进行划分(stable_partition
和DisqualifyPred
),然后对两部分分别处理:
- 对于完成了四轮比赛的选手,根据四轮的总分从低到高对选手进行排序(
for_each
和AggregateScorePred
,sort
和SameRoundPartitionPred
) - 对于没有完成四轮比赛的选手,根据其所完成的轮次、总分和姓名字典序进行排序(
sort
和DiffRoundPartitionPred
)
最后完成4轮比赛的选手分配奖金。由于存在并列的情况,因此可以先确定同时取得该名次的选手的个数TieNum
和取得该名次的业余选手的个数AmateurNum
,然后根据当前百分比索引PercentIndex
计算分配给取得该名次的专业选手的百分比,最后给每个选手分配奖金。
有几个坑需要注意:
- 对所有完成4轮比赛的选手都需要进行排名,并且是需要有序的,也就是说题目中的the order among disqualified players is unimportant是假的
- 选手获得奖金的条件是该选手为专业选手,且还有剩余百分比
- 如果名次并列的选手个数多于剩余百分比的数量,则要将剩余百分比总和所对应的奖金平分给名次并列的所有选手
- 输出
T
的条件是有专业选手同时取得该名次并且专业选手们都获得了奖金,由tie
变量标示 - 并不需要对浮点数所带来的误差进行修正
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
struct Player
{
string name;
size_t position = 0;
bool amateur = false, rank = false, HasPrize = false, tie = false;
vector<int> scores;
int total;
double prize = 0.0;
};
istream &operator>>(istream &is, Player &player)
{
string line, score;
getline(is, line);
player.name.assign(line.substr(0, 20));
while (!player.name.empty() && player.name.back() == ' ') {
player.name.pop_back();
}
player.amateur = player.name.back() == '*';
istringstream iss(line.substr(20));
while (iss >> score) {
if (score != "DQ") {
player.scores.push_back(stoi(score));
}
}
return is;
}
ostream &operator<<(ostream &os, const Player &player)
{
os << left;
os << setw(20) << player.name << ' ';
if (player.tie) {
os << setw(9) << to_string(player.position) + 'T' << ' ';
}
else if (!player.rank) {
os << string(10, ' ');
}
else {
os << setw(9) << to_string(player.position) << ' ';
}
for (size_t i = 0; i < 4; i++)
{
if (i >= player.scores.size()) {
os << string(5, ' ');
}
else {
os << setw(4) << player.scores[i] << ' ';
}
}
if (player.scores.size() != 4) {
os << "DQ";
}
else if (player.amateur || !player.HasPrize) {
os << player.total;
}
else {
os << setw(9) << player.total << ' ';
os << '$';
os << right << fixed << setw(9) << setfill(' ') << setprecision(2) << player.prize;
}
return os;
}
void CutPlayer(vector<Player> &players, size_t &CutNum, size_t &RankNum)
{
size_t round = 2;
auto DisqualifyPred = [&round](const Player &player)->bool
{
return player.scores.size() >= round;
};
auto AggregateScorePred = [&round](Player &player)->void
{
player.total = 0;
for (size_t i = 0; i < round && i < player.scores.size(); i++)
{
player.total += player.scores[i];
}
};
auto SameRoundPartitionPred = [](const Player &p1, const Player &p2)->bool
{
if (p1.total != p2.total) return p1.total < p2.total;
else return p1.name < p2.name;
};
auto DiffRoundPartitionPred = [](const Player &p1, const Player &p2)->bool
{
if (p1.scores.size() != p2.scores.size()) return p1.scores.size() > p2.scores.size();
else if (p1.total != p2.total) return p1.total < p2.total;
else return p1.name < p2.name;
};
auto CutIter = stable_partition(players.begin(), players.end(), DisqualifyPred);
for_each(players.begin(), CutIter, AggregateScorePred);
sort(players.begin(), CutIter, SameRoundPartitionPred);
if (CutIter - players.begin() > 70) {
int TieScore = (players.begin() + 69)->total;
for (auto iter = players.begin() + 70; iter != CutIter; iter++)
{
if (iter->total != TieScore) {
CutIter = iter;
break;
}
}
}
CutNum = CutIter - players.begin();
round = 4;
auto DisqualifiedIter = stable_partition(players.begin(), CutIter, DisqualifyPred);
for_each(players.begin(), DisqualifiedIter, AggregateScorePred);
sort(players.begin(), DisqualifiedIter, SameRoundPartitionPred);
for_each(DisqualifiedIter, CutIter, AggregateScorePred);
sort(DisqualifiedIter, CutIter, DiffRoundPartitionPred);
RankNum = DisqualifiedIter - players.begin();
}
void ProcessTiePlayer(vector<Player> &players, vector<double> percents,
size_t TieIndex, size_t TieNum, size_t AmateurNum, size_t PercentIndex, size_t position, double purse)
{
double PercentSum = 0.0, percent;
size_t PercentNum = TieNum - AmateurNum;
for (size_t j = 0; j < PercentNum && PercentIndex + j < percents.size(); j++)
{
PercentSum += percents[PercentIndex + j];
}
percent = PercentSum / (TieNum - AmateurNum);
for (size_t j = TieIndex; j < TieIndex + TieNum; j++)
{
players[j].rank = true;
players[j].position = position;
players[j].HasPrize = !players[j].amateur && PercentIndex < percents.size();
players[j].tie = players[j].HasPrize && (TieNum - AmateurNum) > 1;
if (players[j].HasPrize) {
players[j].prize = purse * percent / 100.0;
}
}
}
void AllocatePrize(vector<Player> &players, const vector<double> &percents, double purse, size_t RankNum)
{
int TieScore = players[0].total, position = 1;
size_t TieIndex = 0, PercentIndex = 0;
size_t TieNum = 1, AmateurNum = players[0].amateur ? 1 : 0;
for (size_t i = 1; i < RankNum; i++)
{
if (players[i].total == TieScore) {
TieNum++;
if (players[i].amateur) {
AmateurNum++;
}
}
else {
ProcessTiePlayer(players, percents, TieIndex, TieNum, AmateurNum, PercentIndex, position, purse);
TieScore = players[i].total, position += TieNum;
TieIndex = i, PercentIndex += TieNum - AmateurNum;
TieNum = 1, AmateurNum = players[i].amateur ? 1 : 0;
}
}
ProcessTiePlayer(players, percents, TieIndex, TieNum, AmateurNum, PercentIndex, position, purse);
}
void PrintResult(vector<Player> &players, size_t CutNum)
{
cout << "Player Name Place RD1 RD2 RD3 RD4 TOTAL Money Won" << endl;
cout << "-----------------------------------------------------------------------" << endl;
for (size_t i = 0; i < CutNum; i++)
{
cout << players[i] << endl;
}
}
int main()
{
int cases = 0;
cin >> cases;
for (int i = 0; i < cases; i++)
{
double purse = 0.0;
cin >> purse;
vector<double> percents(70, 0.0);
for (size_t i = 0; i < 70; i++)
{
cin >> percents[i];
}
size_t PlayerNum;
cin >> PlayerNum;
cin.get();
vector<Player> players(PlayerNum);
for (size_t i = 0; i < PlayerNum; i++)
{
cin >> players[i];
}
size_t CutNum, RankNum;
CutPlayer(players, CutNum, RankNum);
AllocatePrize(players, percents, purse, RankNum);
PrintResult(players, CutNum);
if (i != cases - 1) {
cout << endl;
}
}
return 0;
}
/*
*/