【题目链接】
ybt 1180:分数线划定
ybt 1946:【09NOIP普及组】分数线划定
OpenJudge NOI 1.10 05:分数线划定
洛谷 P1068 [NOIP2009 普及组] 分数线划定
【题目考点】
1. 排序
【解题思路】
该题要排序的元素个数最大为5000,选用
O
(
n
2
)
O(n^2)
O(n2)的排序算法即可。
基本思路为:先按照“先按成绩降序排序,成绩相同按报名号升序排序”这样的排序规则对输入的数据进行排序,取第
⌊
m
∗
1.5
⌋
\lfloor m*1.5 \rfloor
⌊m∗1.5⌋个人的分数线,再看分数大于等于分数线的人数有多少,再把这些人的信息输出。
可选的写法有:
- 设结构体而后定义结构体变量的排序规则。
- 直接写出排序规则
- 用稳定的排序算法,先按报名号排序,再按成绩排序
【题解代码】
解法1:设结构体,使用sort函数排序
#include <bits/stdc++.h>
using namespace std;
#define N 5005
struct Stu
{
int k, s;//k:报名号 s:成绩
};
bool cmp(Stu &a, Stu &b)
{
if(a.s == b.s)//如果分数相同
return a.k < b.k;//报名号小的在前面
else//如果分数不同
return a.s > b.s;//成绩高的在前面
}
int main()
{
Stu stu[N];
int n, m, line, ct = 0;//line:分数线 ct:人数
cin >> n >> m;
for(int i = 1; i <= n; ++i)
cin >> stu[i].k >> stu[i].s;
sort(stu+1, stu+1+n, cmp);//根据cmp指定的规则进行排序
line = stu[int(m*1.5)].s;//确定分数线
for(int i = 1; i <= n; ++i)
{
if(stu[i].s >= line)
ct++;
}
cout << line << ' ' << ct << endl;
for(int i = 1; i <= ct; ++i)//输出前ct个人的信息
cout << stu[i].k << ' ' << stu[i].s << endl;
return 0;
}
解法2:不用结构体 冒泡排序 直接写出排序规则
#include <bits/stdc++.h>
using namespace std;
#define N 5005
int main()
{
int k[N], s[N], n, m, line, ct = 0;//line:分数线 ct:人数
cin >> n >> m;
for(int i = 1; i <= n; ++i)
cin >> k[i] >> s[i];
for(int i = 1; i <= n - 1; ++i)
for(int j = 1; j <= n - i; ++j)
{
if(s[j] < s[j+1] || s[j] == s[j+1] && k[j] > k[j+1])//如果右面的分数高,或分数相同时右面的编号小,要交换
{
swap(s[j], s[j+1]);
swap(k[j], k[j+1]);
}
}
line = s[int(m*1.5)];//确定分数线
for(int i = 1; i <= n; ++i)
{
if(s[i] >= line)
ct++;
}
cout << line << ' ' << ct << endl;
for(int i = 1; i <= ct; ++i)//输出前ct个人的信息
cout << k[i] << ' ' << s[i] << endl;
return 0;
}
解法3:计数排序+插入排序 使用二维数组记录要输出的数字,而后输出
#include <bits/stdc++.h>
using namespace std;
int score[105][5005] = {};//score[i]:分数为i的各个人的编号 score[i][0]为score[i]这个一维数组的长度
int main()
{
int k, s, n, m, line, ct = 0;//line:分数线 ct:人数
cin >> n >> m;
for(int i = 1; i <= n; ++i)
{
cin >> k >> s;
score[s][++score[s][0]] = k;//把k插入score[s]数组,做插入排序,依编号从小到大排序
for(int j = score[s][0]; j > 1; --j)
{
if(score[s][j] < score[s][j-1])
swap(score[s][j], score[s][j-1]);
else
break;
}
}
int lnum = int(m*1.5);//lnum:第几个人的分数为分数线
for(int i = 100; i >= 0; --i)
{
ct += score[i][0];//分数为i的人有score[i][0]个人
if(ct >= lnum)
{
line = i;
break;
}
}
cout << line << ' ' << ct << endl;
for(int i = 100; i >= line; --i)//输出分数到line的人的信息
for(int j = 1; j <= score[i][0]; ++j)
cout << score[i][j] << ' ' << i << endl;
return 0;
}
解法4:用stable_sort稳定的排序进行两趟排序
选用任意一种稳定的排序都可以
#include <bits/stdc++.h>
using namespace std;
#define N 5005
struct Stu
{
int k, s;//k:报名号 s:成绩
};
bool cmp_k(const Stu &a, const Stu &b)//编号比较规则
{
return a.k < b.k;
}
bool cmp_s(const Stu &a, const Stu &b)//分数比较规则
{
return a.s > b.s;
}
int main()
{
Stu stu[N];
int n, m, line, ct = 0;//line:分数线 ct:人数
cin >> n >> m;
for(int i = 1; i <= n; ++i)
cin >> stu[i].k >> stu[i].s;
stable_sort(stu+1, stu+1+n, cmp_k);//先根据编号比较,再根据分数比较
stable_sort(stu+1, stu+1+n, cmp_s);//先根据编号比较,再根据分数比较
line = stu[int(m*1.5)].s;//确定分数线
for(int i = 1; i <= n; ++i)
{
if(stu[i].s >= line)
ct++;
}
cout << line << ' ' << ct << endl;
for(int i = 1; i <= ct; ++i)//输出前ct个人的信息
cout << stu[i].k << ' ' << stu[i].s << endl;
return 0;
}