P1309 瑞士轮(C++)

题目背景

在双人对决的竞技性比赛,如乒乓球、羽毛球、国际象棋中,最常见的赛制是淘汰赛和循环赛。前者的特点是比赛场数少,每场都紧张刺激,但偶然性较高。后者的特点是较为公平,偶然性较低,但比赛过程往往十分冗长。

本题中介绍的瑞士轮赛制,因最早使用于1895年在瑞士举办的国际象棋比赛而得名。它可以看作是淘汰赛与循环赛的折中,既保证了比赛的稳定性,又能使赛程不至于过长。

题目描述

2×N 名编号为 1∼2N 的选手共进行R 轮比赛。每轮比赛开始前,以及所有比赛结束后,都会按照总分从高到低对选手进行一次排名。选手的总分为第一轮开始前的初始分数加上已参加过的所有比赛的得分和。总分相同的,约定编号较小的选手排名靠前。

每轮比赛的对阵安排与该轮比赛开始前的排名有关:第1 名和第2 名、第 3 名和第 4名、……、第2K−1名和第2K名、…… 、第2N−1名和第2N名,各进行一场比赛。每场比赛胜者得1分,负者得 0分。也就是说除了首轮以外,其它轮比赛的安排均不能事先确定,而是要取决于选手在之前比赛中的表现。

现给定每个选手的初始分数及其实力值,试计算在R 轮比赛过后,排名第Q 的选手编号是多少。我们假设选手的实力值两两不同,且每场比赛中实力值较高的总能获胜。

输入格式

第一行是三个正整数N,R,Q,每两个数之间用一个空格隔开,表示有 2×N名选手、R 轮比赛,以及我们关心的名次 Q。

第二行是2×N 个非负整数s1​,s2,…,s2N​,每两个数之间用一个空格隔开,其中si表示编号为ii 的选手的初始分数。 第三行是2×N 个正整数w1,w 2,…,w2N,每两个数之间用一个空格隔开,其中 wi表示编号为i 的选手的实力值。

输出格式

一个整数,即R 轮比赛结束后,排名第Q 的选手的编号。

输入输出样例

输入 #1

2 4 2
7 6 6 7
10 5 20 15

输出 #1

1

说明/提示

【样例解释】

在这里插入图片描述

【数据范围】

对于30%30%的数据,1≤N≤100;

对于50%50%的数据,1≤N≤10,000;

对于100%100%的数据,1 ≤ N ≤ 100,000,1 ≤ R ≤ 50,1 ≤ Q ≤ 2N,0 ≤ s_1, s_2, …, s_{2N}≤108,1 ≤w_1, w_2 , …, w_{2N}≤ 108

noip2011普及组第3题。

思路

刚开始用的快排sort()一排到底,后来有四个点超时了,然后又换了stable_sort(),结果还是有三个点超时,最后不得不用归并排序,还好有merge()这种神器,不然就难受了。

源码

#include<bits/stdc++.h>
using namespace std;

class person
{
public:
	long long int id, scare, num;
};
person s[200001], a[200001], b[200001];
bool cmp(person a, person b)
{
	if(a.scare != b.scare)
		return a.scare > b.scare;
	return a.id < b.id;
}
int main()
{
	int N, R, Q;
	cin >> N >> R >> Q;
	for (int i = 0; i < 2 * N; i++)
	{
		s[i].id = (long long int)i + 1;
		cin >> s[i].scare;
	}
	for (int i = 0; i < 2 * N; i++)
		cin >> s[i].num;
	sort(s, s + 2 * N, cmp);
	int t1;
	for (int i = 0; i < R; i++)
	{
		t1 = 0;
		for (int j = 0; j < 2 * N; j += 2)
		{
			if (s[j].num > s[j + 1].num)
			{
				a[t1].id = s[j].id;
				a[t1].num = s[j].num;
				a[t1].scare = s[j].scare + 1;
				b[t1].id = s[j+1].id;
				b[t1].num = s[j+1].num;
				b[t1].scare = s[j+1].scare;
				t1++;
			}
			else
			{
				a[t1].id = s[j+1].id;
				a[t1].num = s[j+1].num;
				a[t1].scare = s[j+1].scare + 1;
				b[t1].id = s[j].id;
				b[t1].num = s[j].num;
				b[t1].scare = s[j].scare;
				t1++;
			}
		}
		merge(a, a + t1, b, b + t1, s, cmp);
	}
	cout << s[Q-1].id;
	return 0;
}
<think>好的,我现在需要解决用户提出的关于“C++题目瑞士代码”的问题。首先,我得理解瑞士的机制。瑞士是一种比赛赛制,常见于棋类或电竞比赛,特点是每结束后根据选手的当前积分进行匹配,高分者与高分者对战,避免早期强强相遇,同时保证选手的参赛次数。用户可能需要一个模拟瑞士比赛的C++程序。 接下来,我需要确定瑞士的具体规则。通常,每比赛后,选手按积分排序,然后相邻的选手配对(比如第1对第2,第3对第4)。但可能会有不同的变种,比如积分相同的情况下如何处理,是否允许重复对战等。用户可能没有详细说明,所以可能需要假设基本规则,或者需要程序具备一定的灵活性。 然后,考虑数据结构。选手的信息至少包括积分、胜负情况、可能的唯一标识(如ID)。可能需要一个结构体或类来存储每个选手的数据。例如: struct Player { int id; int score; // 其他可能的属性,如实力值、胜负次数等 }; 接下来,程序的大致流程应该是: 1. 初始化选手列表,可能所有选手初始积分为0。 2. 进行多比赛,每包括: a. 根据当前积分排序选手。 b. 配对选手(相邻配对)。 c. 模拟对战,更新积分。 3. 输出每后的排名或最终结果。 关键点在于如何正确排序和配对。排序需要根据当前积分,可能还要处理相同积分的情况,比如按ID或之前的胜负关系。但题目可能简化处理,积分相同按ID排序。 配对时,通常每将排序后的选手两两配对,比如第一对第二,第三对第四,但需要确保每个选手每只比赛一次。例如,在排序后的列表中,按顺序每两个配对,然后进行比赛。 然后,如何模拟对战?可能需要根据选手的实力值或其他因素决定胜负,或者随机决定。例如,每个选手有一个实力值,对战结果由实力值决定,胜者加分,败者不加。比如,每次胜利加1分。 此外,瑞士可能需要多,直到决出最终排名。但题目可能要求进行固定次,比如R,之后按积分排序。 现在,我需要考虑代码的结构。可能的步骤: - 读取选手数量、数,或者选手的初始数据(如果有实力值)。 - 初始化选手数组。 - 循环每一: - 排序选手。 - 配对。 - 对战,更新积分。 - 最后输出结果。 关于排序,可能需要使用STL的sort函数,自定义比较函数,先按积分降序,积分相同按ID升序。 配对时,需要注意索引的处理。例如,排好序的数组,从第一个开始,每两个为一组,进行对战。 对战处理:假设每个对战的两个选手中,实力高的获胜,积分+1。或者,如果题目有其他规则,比如胜者得3分,平局各得1分,但通常瑞士可能每场必有胜负,所以胜者加分。 可能的代码结构示例: #include <iostream> #include <vector> #include <algorithm> using namespace std; struct Player { int id; int score; int strength; // 假设有实力值决定胜负 }; bool comparePlayers(const Player &a, const Player &b) { if (a.score != b.score) { return a.score > b.score; } else { return a.id < b.id; } } void swissRound(vector<Player> &players) { // 每比赛 sort(players.begin(), players.end(), comparePlayers); // 配对并比赛 for (int i = 0; i < players.size(); i += 2) { if (i+1 >= players.size()) break; // 处理奇数情况 Player &p1 = players[i]; Player &p2 = players[i+1]; if (p1.strength > p2.strength) { p1.score++; } else { p2.score++; } } } int main() { int N, R; cin >> N >> R; vector<Player> players(N); // 初始化,假设输入每个选手的实力值,id从1开始 for (int i = 0; i < N; ++i) { players[i].id = i + 1; cin >> players[i].strength; players[i].score = 0; } // 进行R比赛 for (int r = 0; r < R; ++r) { swissRound(players); } // 输出最终排名 sort(players.begin(), players.end(), comparePlayers); for (const auto &p : players) { cout << p.id << " "; } return 0; } 不过,这可能存在一些问题。比如,在每比赛中,排序后立即配对,但瑞士的正确配对方式是否如此?或者是否需要更复杂的配对逻辑,比如避免重复对战?例如,在真实的瑞士中,每配对需要确保选手尽可能与相同积分的对手比赛,并且之前未对战过。但题目可能简化处理,如每次按排序后的顺序配对,而不管是否之前对战过。 此外,上述代码在每排序后,直接按顺序两两配对,可能导致同一对选手多次对战,这在实际情况中可能不被允许。但题目是否要求避免这点?如果用户的问题中没有特别说明,可能可以忽略这点,或者需要进一步询问。 另外,选手的数量是否为偶数?如果是奇数,如何处理空的情况?比如,最后一名空,自动加1分。上述代码在处理奇数时,会跳过最后一个选手,但并未处理其得分。可能需要调整。 例如,当选手数为奇数时,最后一名单出来,可能自动获胜或者获得积分。这需要具体规则,但代码中可能需要处理这种情况。 综上,用户的提问可能希望得到一个瑞士的基本实现,但需要明确具体规则。根据常见瑞士的简化版,上述代码可能是一个起点,但可能需要调整配对逻辑,避免重复对战,处理奇数情况等。 可能的改进点: 1. 每配对时,确保同一对选手不会重复对战。这需要记录每对选手的对战历史,但会增加复杂度。可能题目不要求,所以暂时忽略。 2. 处理奇数选手时的空情况。例如,最后一名的选手本不比赛,积分不变,或者自动加分? 3. 更正确的配对方式:在积分相同的情况下,如何正确分组,然后进行配对。 但考虑到用户可能只需要一个基本的瑞士代码示例,上述代码可能足够,但需要指出可能的限制和假设。</think>以下是一个基于瑞士赛制的简化版C++代码实现,包含详细注释和分步解释: ### 问题分析 瑞士的核心流程: 1. **初始化**:读取选手数据并设置初始积分 2. **多比赛**: - 按积分排序选手 - 相邻选手进行配对 - 根据比赛结果更新积分 3. **结果输出**:显示最终排名 ### 代码实现 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; // 选手结构体 struct Player { int id; // 选手编号 int score = 0; // 当前积分 int strength; // 比赛实力值 }; // 排序比较函数(先按积分降序,再按编号升序) bool comparePlayers(const Player &a, const Player &b) { if (a.score != b.score) return a.score > b.score; return a.id < b.id; } // 进行一瑞士比赛 void runSwissRound(vector<Player> &players) { // 第一步:排序选手 sort(players.begin(), players.end(), comparePlayers); // 第二步:配对比赛 for (int i = 0; i < players.size(); i += 2) { // 处理奇数选手情况 if (i+1 >= players.size()) { // players[i].score += 1; // 空加分(按需开启) break; } // 获取对战双方 Player &p1 = players[i]; Player &p2 = players[i+1]; // 根据实力值决定胜负 if (p1.strength > p2.strength) { p1.score += 1; } else { p2.score += 1; } } } int main() { int N, R; // 选手数量和比赛数 cin >> N >> R; vector<Player> players(N); // 初始化选手数据 for (int i = 0; i < N; ++i) { players[i].id = i + 1; // 编号从1开始 cin >> players[i].strength; // 输入实力值 } // 进行R比赛 for (int round = 0; round < R; ++round) { runSwissRound(players); } // 输出最终排名 sort(players.begin(), players.end(), comparePlayers); for (const auto &p : players) { cout << p.id << " "; } return 0; } ``` ### 关键点解析 1. **数据结构设计**: ```cpp struct Player { int id; int score = 0; int strength; }; ``` - `id` 用于唯一标识选手 - `score` 记录累计积分 - `strength` 决定比赛胜负的核心属性 2. **排序规则**: ```cpp bool comparePlayers(...) { if (积分不同) 按积分降序排列; else 按编号升序排列; } ``` 确保相同积分选手的稳定排序 3. **比赛流程**: ```cpp void runSwissRound(...) { 1. 排序选手 2. 两两配对比赛 for (i从0开始,步长2) { if (到达末尾) break; 根据strength比较结果加分 } } ``` 4. **特殊处理**: - 奇数选手时的空处理(代码中注释部分) - 每比赛前必须重新排序 ### 测试示例 输入: ``` 4 2 // 4个选手进行2比赛 100 // 选手1实力值 80 // 选手2实力值 90 // 选手3实力值 70 // 选手4实力值 ``` 运行过程: ``` 初始状态: 选手 [1(0), 2(0), 3(0), 4(0)] 第1: 排序后:1,2,3,4(同分按编号) 比赛配对: 1 vs2 → 1胜(100>80) 3 vs4 →3胜(90>70) 积分变为:1(1), 2(0), 3(1), 4(0) 第2: 排序:1(1),3(1),2(0),4(0) 比赛配对: 1 vs3 →1胜(100>90) 2 vs4 →2胜(80>70) 最终积分:1(2),3(1),2(1),4(0) 输出排序:1 3 2 4 ``` ### 扩展建议 1. **增加对战记录**:防止重复对战 2. **完善积分规则**:支持多种加分方式 3. **优化配对算法**:实现更精确的瑞士配对规则 4. **异常处理**:增加输入校验和错误处理 这个实现展示了瑞士的核心逻辑,可根据具体题目要求进行功能扩展和规则调整。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chaoql

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值