题解:洛谷 P1786 帮贡排序
【题目来源】
洛谷:P1786 帮贡排序 - 洛谷(luogu.com.cn)
【题目描述】
目前帮派内共最多有一位帮主,两位副帮主,两位护法,四位长老,七位堂主,二十五名精英,帮众若干。
现在 absi2011 要对帮派内几乎所有人的职位全部调整一番。他发现这是个很难的事情。于是要求你帮他调整。
他给你每个人的以下数据:
他的名字(长度不会超过 \(30\)),他的原来职位,他的帮贡,他的等级。
他要给帮贡最多的护法的职位,其次长老,以此类推。
可是,乐斗的显示并不按帮贡排序而按职位和等级排序。
他要你求出最后乐斗显示的列表(在他调整过职位后):职位第一关键字,等级第二关键字。
注意:absi2011 无权调整帮主、副帮主的职位,包括他自己的(这不是废话么..)
他按原来的顺序给你(所以,等级相同的,原来靠前的现在也要靠前,因为经验高低的原因,但此处为了简单点省去经验。)
【输入】
第一行一个正整数 \(n\),表示星月家园内帮友的人数。
下面 \(n\) 行每行两个字符串两个整数,表示每个人的名字、职位、帮贡、等级。
【输出】
一共输出 \(n\) 行,每行包括排序后乐斗显示的名字、职位、等级。
【输入样例】
9
DrangonflyKang BangZhu 100000 66
RenZaiJiangHu FuBangZhu 80000 60
absi2011 FuBangZhu 90000 60
BingQiLingDeYanLei HuFa 89000 58
Lcey HuFa 30000 49
BangYou3 ZhangLao 1000 1
BangYou1 TangZhu 100 40
BangYou2 JingYing 40000 10
BangYou4 BangZhong 400 1
【输出样例】
DrangonflyKang BangZhu 66
RenZaiJiangHu FuBangZhu 60
absi2011 FuBangZhu 60
BingQiLingDeYanLei HuFa 58
BangYou2 HuFa 10
Lcey ZhangLao 49
BangYou1 ZhangLao 40
BangYou3 ZhangLao 1
BangYou4 ZhangLao 1
【核心思想】
-
问题分析:给定一个帮派成员列表,包含姓名、原职位、帮贡和等级。帮主和副帮主职位不可调整,其余成员需根据帮贡从高到低重新分配职位(职位名额固定:护法2人、长老4人、堂主7人、精英25人,其余为帮众)。最后要求按职位优先级(从高到低)、等级从高到低、输入顺序排序并输出。本质是一个多关键字排序 + 职位分配问题。
-
算法选择:
- 两次排序:
- 第一次排序:对除帮主和副帮主外的成员按帮贡降序、输入顺序升序排序,然后按固定名额分配职位(护法、长老、堂主、精英、帮众)。
- 第二次排序:对所有成员按职位优先级(帮主→副帮主→护法→长老→堂主→精英→帮众)、等级降序、输入顺序升序排序,输出。
- 职位优先级编码:使用函数
sti(zw)将职位映射为数字(0~6),便于排序比较。
- 两次排序:
-
关键步骤:
- 读入:读取 \(n\) 和每个成员的信息,记录输入顺序(
sx)。 - 第一轮排序:对索引从 3 到 \(n-1\) 的成员(即除帮主和副帮主外)按帮贡降序、输入顺序升序排序。
- 职位分配:遍历排序后的成员,根据其在列表中的位置(从 0 开始)分配职位:0~1 护法,2~5 长老,6~12 堂主,13~37 精英,其余帮众。
- 第二轮排序:对所有成员按职位优先级升序、等级降序、输入顺序升序排序。
- 输出:按排序后的顺序输出姓名、职位、等级。
- 读入:读取 \(n\) 和每个成员的信息,记录输入顺序(
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),两次排序。
- 空间复杂度:\(O(n)\)。
-
多关键字排序与职位映射:
- 稳定排序:在帮贡相同时,按输入顺序排序,保证了原有的相对顺序。
- 职位映射:将职位字符串转换为整数,使得排序时能按优先级比较。
- 名额分配:帮主和副帮主固定,其余职位按名额依次分配。
- 适用场景:这类“多级排序 + 名额分配”问题,常见于模拟题和系统设计类题目。
【算法标签】
普及 #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
// 定义结构体,表示每个人的信息
struct people
{
string name; // 姓名
string zw; // 职位
int bg; // 贡献值
int dj; // 等级
int sx; // 序号(输入顺序)
} p[115]; // 最多115个人
// 比较函数1:用于排序贡献值和序号
bool cmp1(people p1, people p2)
{
if (p1.bg != p2.bg) return p1.bg > p2.bg; // 贡献值高的排前面
return p1.sx < p2.sx; // 贡献值相同,序号小的排前面
}
// 将序号转换为职位名称
string its(int n)
{
if (n == 0) return "BangZhu"; // 序号0是帮主
if (n <= 2) return "FuBangZhu"; // 序号1-2是副帮主
if (n <= 4) return "HuFa"; // 序号3-4是护法
if (n <= 8) return "ZhangLao"; // 序号5-8是长老
if (n <= 15) return "TangZhu"; // 序号9-15是堂主
if (n <= 40) return "JingYing"; // 序号16-40是精英
return "BangZhong"; // 其余是帮众
}
// 将职位名称转换为序号
int sti(string s)
{
if (s == "BangZhu") return 0; // 帮主
if (s == "FuBangZhu") return 1; // 副帮主
if (s == "HuFa") return 2; // 护法
if (s == "ZhangLao") return 3; // 长老
if (s == "TangZhu") return 4; // 堂主
if (s == "JingYing") return 5; // 精英
return 6; // 帮众
}
// 比较函数2:用于排序职位、等级和序号
bool cmp2(people p1, people p2)
{
int tmp1 = sti(p1.zw), tmp2 = sti(p2.zw); // 将职位转换为序号
if (tmp1 != tmp2) return tmp1 < tmp2; // 职位序号小的排前面
if (p1.dj != p2.dj) return p1.dj > p2.dj; // 职位相同,等级高的排前面
return p1.sx < p2.sx; // 等级相同,序号小的排前面
}
int main()
{
int n; // 人数
cin >> n; // 输入人数
for (int i = 0; i < n; i++)
{
cin >> p[i].name >> p[i].zw >> p[i].bg >> p[i].dj; // 输入每个人的信息
p[i].sx = i; // 记录输入顺序
}
// 对第4个人到第n个人按贡献值和序号排序(前3个人是帮主和副帮主,不参与排序)
sort(p + 3, p + n, cmp1);
// 根据排序后的序号分配职位
for (int i = 0; i < n; i++)
p[i].zw = its(i); // 将序号转换为职位名称
// 对第4个人到第n个人按职位、等级和序号排序
sort(p + 3, p + n, cmp2);
// 输出每个人的信息
for (int i = 0; i < n; i++)
cout << p[i].name << " " << p[i].zw << " " << p[i].dj << endl;
return 0;
}
【运行结果】
9
DrangonflyKang BangZhu 100000 66
RenZaiJiangHu FuBangZhu 80000 60
absi2011 FuBangZhu 90000 60
BingQiLingDeYanLei HuFa 89000 58
Lcey HuFa 30000 49
BangYou3 ZhangLao 1000 1
BangYou1 TangZhu 100 40
BangYou2 JingYing 40000 10
BangYou4 BangZhong 400 1
DrangonflyKang BangZhu 66
RenZaiJiangHu FuBangZhu 60
absi2011 FuBangZhu 60
BingQiLingDeYanLei HuFa 58
BangYou2 HuFa 10
Lcey ZhangLao 49
BangYou1 ZhangLao 40
BangYou3 ZhangLao 1
BangYou4 ZhangLao 1
浙公网安备 33010602011771号