UVA12412 题解

非常良心的题啊,我就只交了 25+ 次就 AC 了啊,推荐推荐!其实就是C艹精度问题搞了我好久/kk

思路

原题样例输出有误,正确输出

简述一下题目大意:

一开始有一个主界面,主界面共有 \(6\) 种操作,分别为:

  1. 添加学生
  2. 删除学生
  3. 查询学生(通过 sid 或者是 姓名)
  4. 显示排名
  5. 显示状态
  6. 退出

再来说一下题目的三个坑点(我自己跳进去过的几个):

  1. 样例输出有误
  2. 显示排名 PDF 输出中的 ' 是全角字符,而答案应该是半角字符。
  3. 显示状态计算平均数的时候会出现精度误差,需要加上一个 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,不讲了。

其他东西

我的代码

数据生成代码

posted @ 2021-11-19 21:03  MituFun  阅读(124)  评论(0)    收藏  举报