算法竞赛入门经典 例题5-10

该博客详细解析了一道关于PGA高尔夫联赛奖金分配的算法问题。内容包括选手晋级规则、分数计算、奖金分配策略以及处理并列名次的方法。通过对选手数据的操作,如稳定划分、排序和计算总分,确定了前70名选手及其奖金分配。文章还讨论了算法实现中的注意事项和陷阱,为解决此类问题提供了思路。
摘要由CSDN通过智能技术生成

UVa207

PGA Tour Prize Money

根据专业高尔夫联赛(Professional Golf Association,PGA)的排名规则对选手进行排名:

  1. 每个选手至少打两轮比赛,除非在前两轮中被取消了资格
  2. 如果在某一轮中被取消了资格,则终止后面的比赛,在前两轮中被取消资格的选手将不能晋级。如果在晋级后两轮中被取消了资格则不能获得奖金
  3. 前两轮结束后,将有70名选手晋级,如果出现并列的情况,也可以有多于70名选手晋级
  4. 4轮全部打完的选手将根据4轮的总分数进行排名,总分数越低排名越高
  5. 排名后选手获得的奖金由该名次对应的百分比确定
  6. 输入数据保证只有一个第一名
  7. 如果在后续名次(第2到第70名)中出现了并列的情况,则名次并列选手所获奖金的百分比为并列名次对应的百分比的均值
  8. 如果可以获得奖金的选手少于70人,则剩余的奖金将不发放
  9. 业余选手不获得奖金,即业余选手名次所对应的百分比顺延到下一位专业选手
  10. 排名前70以及与晋级的最后一名选手并列的选手可以获得奖金

只输出晋级后的选手,排名从高到低,在后两轮中被取消资格的选手在最后输出,按照其所打的轮次、总分和姓名字典序的优先级进行排名。

这道题没有输入输出样例,同时uDebug上的测试用例覆盖也不够全面,因此调试起来非常困难。

首先确定可以晋级的选手,分四步完成:

  1. 根据选手是否完成了前两轮比赛来对选手们进行划分(stable_partitionDisqualifyPred
  2. 计算完成了前两轮的选手的总分(for_eachAggregateScorePred
  3. 根据总分从低到高进行排序(sortSameRoundPartitionPred
  4. 选出前70名和并列最后一名的选手,这就是需要输出的所有选手

其次要对晋级的选手进行排序。由于只有完成4轮比赛才有可能获得奖金,因此可以先根据是否完成了4轮比赛来对晋级后的选手进行划分(stable_partitionDisqualifyPred),然后对两部分分别处理:

  • 对于完成了四轮比赛的选手,根据四轮的总分从低到高对选手进行排序(for_eachAggregateScorePredsortSameRoundPartitionPred
  • 对于没有完成四轮比赛的选手,根据其所完成的轮次、总分和姓名字典序进行排序(sortDiffRoundPartitionPred

最后完成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;
}
/*

*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值