UVA12412 题解
非常良心的题啊,我就只交了 25+ 次就 AC 了啊,推荐推荐!其实就是C艹精度问题搞了我好久/kk
思路
原题样例输出有误,正确输出。
简述一下题目大意:
一开始有一个主界面,主界面共有 \(6\) 种操作,分别为:
- 添加学生
- 删除学生
- 查询学生(通过 sid 或者是 姓名)
- 显示排名
- 显示状态
- 退出
再来说一下题目的三个坑点(我自己跳进去过的几个):
- 样例输出有误
- 显示排名 PDF 输出中的
'是全角字符,而答案应该是半角字符。 - 显示状态计算平均数的时候会出现精度误差,需要加上一个 eps。(这个花了我好多时间调)
下面我们来逐个击破。
1. 添加学生
对于题目的信息,我们可以使用一个结构体来存储。
这个结构体应该包含以下内容:
- 排名
- sid
- cid
- 姓名
- 四个科目的成绩
- 总分
- 平均分
- 第几个输入的(因为之后删除会打乱顺序,所以需要重新排序)
还要有一个读入的函数,可以放在结构体里面:
int cnt;
struct Stu {
int rk;
string sid, cid;
string name;
int s1, s2, s3, s4;
int tot;
double avg;
int id;
bool read() {
cin >> sid;
if (sid == "0") {
return false;
}
cin >> cid >> name >> s1 >> s2 >> s3 >> s4;
tot = s1 + s2 + s3 + s4;
avg = (s1 + s2 + s3 + s4) / 4.00;
id = ++cnt;
return true;
}
};
因为添加学生的时候需要判断 sid 是否重复,这个很好实现,只需要从第一个学生到最后一个学生枚举一遍,如果发现了有重复的 sid 那就返回 false;函数最后返回 true。
bool checkSID(string sid) {
for (int i = 0;i < (int)info.size();i++) {
if (sid == info[i].sid) {
return false;
}
}
return true;
}
这里使用 vector 存储学生信息是为了方便之后删除学生信息(因为 vector 自带了一个 pop_back 函数)。
Main 函数代码:
while (1) {
cout << "Please enter the SID, CID, name and four scores. Enter 0 to finish." << endl;
Stu tmp;
if (!tmp.read()) {
break; // 结束输入
} else {
if (!checkSID(tmp.sid)) {
cout << "Duplicated SID." << endl; // 出现重复 sid
} else {
info.push_back(tmp); // 添加信息
}
}
}
2. 删除学生
对于删除一个学生,可以将这个学生和 vector 中最后一个元素调换,然后 pop_back 即可。体现了 vector 的方便性。
int remove(string str) {
int amt = 0; // 删除个数
for (int i = 0;i < (int)info.size();i++) {
if (info[i].name == str || info[i].sid == str) {
swap(info[i], info[(int)info.size() - 1]);
info.pop_back();
amt++;
i--; // 注意,因为减少了一个元素,所以 i 需要减少一位
sort(info.begin(), info.end(), cmp); // 重新排序
}
}
return amt;
}
Main 函数代码:
while (1) {
cout << "Please enter SID or name. Enter 0 to finish." << endl;
string str;
cin >> str;
if (str == "0") {
break;
} else {
cout << remove(str) << " student(s) removed." << endl;
}
}
3. 查询学生
这个最麻烦的就是算排名。其实大可不必搞一个排序排来排去,计算比当前学生总分高的人数就是该学生的排名。
为了让代码更加美观,可以先用一个 vector 储存满足条件的所有学生,最后统一输出。
void query(string str) {
vector<Stu> val;
for (int i = 0;i < (int)info.size();i++) {
if (info[i].name == str || info[i].sid == str) {
Stu now;
now = info[i];
now.rk = 1; // 注意初始化应该为 1
for (int j = 0;j < (int)info.size();j++) { // 计算排名
if (info[j].tot > now.tot) { // 总分比他大
now.rk++;
}
}
val.push_back(now);
}
}
for (int i = 0;i < (int)val.size();i++) {
cout << val[i].rk << ' ' << val[i].sid << ' ' << val[i].cid << ' ' << val[i].name << ' ' << val[i].s1 << ' ' << val[i].s2 << ' ' << val[i].s3 << ' ' << val[i].s4 << ' ' << val[i].tot << ' ' << val[i].avg << endl; // 统一输出
}
}
Main 函数代码:
while (1) {
cout << "Please enter SID or name. Enter 0 to finish." << endl;
string str;
cin >> str;
if (str == "0") {
break;
} else {
query(str);
}
}
4. 显示排名
啥也不干,直接输出 Showing the ranklist hurts students' self-esteem. Don't do that. 即可。
※但是但是,题目中的 ' 是中文的,需要转化为英文的,否则会 WA!!!※
5. 输出状态
代码量比较大的地方,我提交了很多次都 WA 了就是因为这个地方。(注意 double 精度丢失的问题)
思维难度很低,直接挨个扫一遍,然后满足条件的就统计。最后输出即可。
典型的 WA 代码:
int s1tot, s2tot, s3tot, s4tot;
double s1avg, s2avg, s3avg, s4avg;
int s1p, s2p, s3p, s4p;
int s1f, s2f, s3f, s4f;
int p4, p3, p2, p1, p0;
void stat(string str) {
s1tot = s2tot = s3tot = s4tot = 0;
s1avg = s2avg = s3avg = s4avg = 0.0;
s1p = s2p = s3p = s4p = 0;
s1f = s2f = s3f = s4f = 0;
p4 = p3 = p2 = p1 = p0 = 0;
int amt = 0;
for (int i = 0;i < (int)info.size();i++) {
if (info[i].cid == str || str == "0") {
int pass = 0;
amt++;
s1tot += info[i].s1;
s2tot += info[i].s2;
s3tot += info[i].s3;
s4tot += info[i].s4;
if (info[i].s1 >= 60) {
s1p++;
pass++;
} else {
s1f++;
}
if (info[i].s2 >= 60) {
s2p++;
pass++;
} else {
s2f++;
}
if (info[i].s3 >= 60) {
s3p++;
pass++;
} else {
s3f++;
}
if (info[i].s4 >= 60) {
s4p++;
pass++;
} else {
s4f++;
}
if (pass == 0) p0++;
if (pass == 1) p1++;
if (pass == 2) p2++;
if (pass == 3) p3++;
if (pass == 4) p4++;
}
}
s1avg = s1tot / (amt * 1.0);
s2avg = s2tot / (amt * 1.0);
s3avg = s3tot / (amt * 1.0);
s4avg = s4tot / (amt * 1.0);
cout << fixed << setprecision(2);
cout << "Chinese" << endl;
cout << "Average Score: " << s1avg << endl;
cout << "Number of passed students: " << s1p << endl;
cout << "Number of failed students: " << s1f << endl << endl;
cout << "Mathematics" << endl;
cout << "Average Score: " << s2avg << endl;
cout << "Number of passed students: " << s2p << endl;
cout << "Number of failed students: " << s2f << endl << endl;
cout << "English" << endl;
cout << "Average Score: " << s3avg << endl;
cout << "Number of passed students: " << s3p << endl;
cout << "Number of failed students: " << s3f << endl << endl;
cout << "Programming" << endl;
cout << "Average Score: " << s4avg << endl;
cout << "Number of passed students: " << s4p << endl;
cout << "Number of failed students: " << s4f << endl << endl;
cout << "Overall:" << endl;
cout << "Number of students who passed all subjects: " << p4 << endl;
cout << "Number of students who passed 3 or more subjects: " << p3 + p4 << endl;
cout << "Number of students who passed 2 or more subjects: " << p2 + p3 + p4 << endl;
cout << "Number of students who passed 1 or more subjects: " << p1 + p2 + p3 + p4 << endl;
cout << "Number of students who failed all subjects: " << p0 << endl << endl;
return;
}
如果使用这段代码交上去的话,会 WA,因为什么呢?C++ 的 double 类型在计算的时候会出现精度误差。就比如正确答案应该是 13.215 的时候,有精度误差可能就会变为 13.2149999999。此时如果保留 \(2\) 位小数输出的话,就会变成 13.21,而正确答案是 13.22。
所以要加上一个 eps(普遍取值为 1e-5)。是一个极小的浮点数,加上他 13.2149999999 就会变成 13.2150049999,此时保留 \(2\) 位小数就是 13.22。是正确的输出。
在
s1avg = s1tot / (amt * 1.0);
s2avg = s2tot / (amt * 1.0);
s3avg = s3tot / (amt * 1.0);
s4avg = s4tot / (amt * 1.0);
后面加上
s1avg += eps;
s2avg += eps;
s3avg += eps;
s4avg += eps;
即可。
6. 退出
直接 break,不讲了。

浙公网安备 33010602011771号