洛谷:P5741:旗鼓相当的对手 - 加强版


洛谷:P5741:旗鼓相当的对手 - 加强版

题目

P5741:旗鼓相当的对手 - 加强版

分析

如果你熟悉结构,那么本题其实没啥可以讲解的,所以我决定增加本题的技术含量。

结构体内的成员函数

C++的结构体中,除了声明成员变量外,还可以声明成员函数,使得它和class(类)越来越相似。

我们这个结构体需要声明怎样的成员函数(“操作”)呢?

构造函数

结构的构造函数很方便,可以大大简化我们为结构内的成员赋值的操作。

如果不用构造函数,那么我们读入姓名以及三科成绩后,可能的操作就是:

cin >> name >> chinese >> math >> english;
students[i].name = name;
students[i].chinese = chinese;
students[i].math = math;
students[i].english = english;

代码长且容易写错,更不符合“面向对象”的编程建议。

我们可以声明构造函数来简化这个过程。通常我们会声明两个:一个是缺省的构造函数,一个是带参数的构造函数:

struct Student 
{
    string name; 
    int chinese, math, english;

    Student() : name(""), chinese(0), math(0), english(0) {}
    Student(string n, int c, int m, int e) : name(n), chinese(c), math(m), english(e) {}
    // ...
}

缺省构造函数不接受任何参数,但为成员变量赋予了有意义的缺省值。

带参数的构造函数用参数值为各个成员变量赋值。于是在我们的代码中,就可以改写为:

string name;
int chinese, math, english;
cin >> name >> chinese >> math >> english;
students[i] = Student(name, chinese, math, english);

这是更C++的写法。

求总分

题目要求对总分进行比较,我们当然可以用代码求出两个学生的总分然后比较。但更好的写法是将这个操作封装为一个成员函数:

struct Student
{
    // ...
    int getTotal() const
    {
        return chinese + math + english;
    }
    //...
}

这么做的好处是,如果未来我们要对getTotal()进行改写,只要改动一处即可。

检查差异函数

我们要对四个成绩(三个单科和总分)进行比较,看看是否都在一个“范围内”。这可以看成是对“一个”成绩进行了四次相似的操作:\(abs(a-b)\le range\),所以应该考虑将这个操作封装为一个函数。

struct Student
{
    // ...
    bool checkDiff(int a, int b, int range) const
    {
        return abs(a - b) <= range;
    }
    // ...
}

这样我们就可以四次调用这个函数,分别传入三门单科成绩和总分以及对应的“范围”。

判定相等

我们当然可以按照上述的做法,定义一个诸如checkEqual的函数,但也许更“直观”的做法,是重载作用于这个结构的==运算符。

struct Student
{
    // ...
    bool operator==(const Student& a) const
    {
        if (checkDiff(a.chinese, chinese, 5) && checkDiff(a.math, math, 5) &&
            checkDiff(a.english, english, 5) &&
            checkDiff(getTotal(), a.getTotal(), 10))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
};

我们重新定义了Student这个结构体的==操作。其实现就是题目中的要求:三科成绩都相差5分以内,总成绩相差10分以内。

于是,我们就可以很优雅地用这个新的==来判定两个学生是不是“旗鼓相当”,因为我们重新定义了==的含义。

if (students[i] == students[j])
{
    cout << students[i].name << " " << students[j].name << endl;
}

是不是很直观?

答案

Solution

思考

本题可以用最基本的C++完成。我这里的写法倒不是故意炫技,而是提醒大家,C++的一些基本特性和写法,是推荐的写法。

当然,这里的代码还并不是最C++的代码,有兴趣的读者可以自行加以修改,使得其更符合C++风格。

Previous Next