diff --git a/content/posts/bj_homework.md b/content/posts/bj_homework.md index c3b3763..b7b5f3e 100644 --- a/content/posts/bj_homework.md +++ b/content/posts/bj_homework.md @@ -945,28 +945,52 @@ int main(int argc, char *argv[]) { ### 编程题2 {#编程题2} -> 魔兽世界之一:备战描述魔兽世界的西面是红魔军的司令部,东面是蓝魔军的司令部。两个司令部之间是依次排列的若干城市。红司令部,City 1,City 2,........,City n,蓝司令部两军的司令部都会制造武士。武士一共有 dragon 、ninja、iceman、lion、wolf 五种。每种武士都有编号、生命值、攻击力这三种属性。双方的武士编号都是从1开始计算。红方制造出来的第n个武士,编号就是n。同样,蓝方制造出来的第n个武士,编号也是n。武士在刚降生的时候有一个生命值。在每个整点,双方的司令部中各有一个武士降生。红方司令部按照iceman、lion、wolf、ninja、dragon的顺序循环制造武士。蓝方司令部按照lion、dragon、ninja、iceman、wolf的顺序循环制造武士。制造武士需要生命元。制造一个初始生命值为m的武士,司令部中的生命元就要减少m个。如果司令部中的生命元不足以制造某个按顺序应该制造的武士,那么司令部就试图制造下一个。如果所有武士都不能制造了,则司令部停止制造武士。给定一个时间,和双方司令部的初始生命元数目,要求你将从0点0分开始到双方司令部停止制造武士为止的所有事件按顺序输出。一共有两种事件,其对应的输出样例如下: +> 魔兽世界之一:备战描述魔兽世界的西面是红魔军的司令部,东面是蓝魔军的司令部。两个司令部之间是依次排列的若干城市。 +> +> 红司令部,City 1,City 2,........,City n,蓝司令部 +> +> 两军的司令部都会制造武士。武士一共有 dragon 、ninja、iceman、lion、wolf 五种。每种武士都有编号、生命值、攻击力这三种属性。双方的武士编号都是从1开始计算。红方制造出来的第n个武士,编号就是n。同样,蓝方制造出来的第n个武士,编号也是n。 +> +> 武士在刚降生的时候有一个生命值。在每个整点,双方的司令部中各有一个武士降生。红方司令部按照iceman、lion、wolf、ninja、dragon的顺序循环制造武士。蓝方司令部按照lion、dragon、ninja、iceman、wolf的顺序循环制造武士。制造武士需要生命元。制造一个初始生命值为m的武士,司令部中的生命元就要减少m个。如果司令部中的生命元不足以制造某个按顺序应该制造的武士,那么司令部就试图制造下一个。如果所有武士都不能制造了,则司令部停止制造武士。给定一个时间,和双方司令部的初始生命元数目,要求你将从0点0分开始到双方司令部停止制造武士为止的所有事件按顺序输出。一共有两种事件,其对应的输出样例如下: > > 1. 武士降生输出样例: 004 blue lion 5 born with strength 5,2 lion in red headquarter +> > 表示在4点整,编号为5的蓝魔lion武士降生,它降生时生命值为5,降生后蓝魔司令部里共有2个lion武士。(为简单起见,不考虑单词的复数形式)注意,每制造出一个新的武士,都要输出此时司令部里共有多少个该种武士。 +> > 2. 司令部停止制造武士输出样例: 010 red headquarter stops making warriors +> > 表示在10点整,红方司令部停止制造武士 > > 输出事件时:首先按时间顺序输出;同一时间发生的事件,先输出红司令部的,再输出蓝司令部的。输入第一行是一个整数,代表测试数据组数。每组测试数据共两行。第一行:一个整数M。其含义为, 每个司令部一开始都有M个生命元( 1 <= M <= 10000)。第二行:五个整数,依次是 dragon 、ninja、iceman、lion、wolf 的初始生命值。它们都大于0小于等于10000。输出对每组测试数据,要求输出从0时0分开始,到双方司令部都停止制造武士为止的所有事件。对每组测试数据,首先输出"Case:n" n是测试数据的编号,从1开始 。接下来按恰当的顺序和格式输出所有事件。每个事件都以事件发生的时间开头,时间以小时为单位,有三位。样例输入 +> > 1 +> > 20 +> > 3 4 5 6 7 +> > 样例输出 +> > Case:1 +> > 000 red iceman 1 born with strength 5,1 iceman in red headquarter +> > 000 blue lion 1 born with strength 6,1 lion in blue headquarter +> > 001 red lion 2 born with strength 6,1 lion in red headquarter +> > 001 blue dragon 2 born with strength 3,1 dragon in blue headquarter +> > 002 red wolf 3 born with strength 7,1 wolf in red headquarter +> > 002 blue ninja 3 born with strength 4,1 ninja in blue headquarter +> > 003 red headquarter stops making warriors +> > 003 blue iceman 4 born with strength 5,1 iceman in blue headquarter +> > 004 blue headquarter stops making warriors +> 这道题题面比较复杂。但是仔细分析后可知指挥部只有制造武士这一种行为,实际上比较简单。只需读取输入为每个武士赋予相应的属性即可。这里因为武士本身没有行为,可以使用结构体存储。司令部为一个类,其中包含了生命元数量,制造武士顺序,现在制造的武士的下标,司令部的颜色,武士的标号和所有武士的信息这几个成员变量。使用武士名作为key构造哈希表存储武士信息可以快速获得某种武士的数量。构造函数中初始化成员变量。成员函数为制作武士和判断两个函数。制造武士用于根据指定下标制造对应顺序的武士,判断则用于判断当前应制造的武士能否制造,不能则寻找下一个能制造的武士,如果已经不能制造则终止制造武士并将over置为true; 代码如下 @@ -1118,3 +1142,428 @@ int main(int argc, char *argv[]) { 运行结果如下 {{< figure src="https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/10290QUmPy.png" >}} + +## 第五次作业 {#第五次作业} + +### 完善字符串类 {#完善字符串类} +> 编写封装完善的MyString类并测试,要求有多个构造函数、完成深拷贝的拷贝构造函数和赋值运算符函数以及一些其他功能函数;练习掌握内联函数的使用。 + +该题为完善之前的字符串类,需要为之前的字符串类添加深拷贝的拷贝构造函数,即在拷贝字符串的过程中为指针申请新的内存空间。同时还要重载赋值运算符‘=’,在重载运算符的过程中,若想直接修改赋值运算符的左值,则需使用this指针指向调用运算符的对象本身。重载的运算符可以看做对象的成员函数,即`object.operator=()`。返回对象本身即返回*this,对this指针解引用即可得到对象本身,代码如下 + +```c++ +// MyString.h +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +class MyString { +private: + char *str; + int length; + +public: + MyString(); + MyString(char *s); + MyString(const MyString &obj); + MyString &operator=(const MyString &obj); + char *get_string(); + void set_string(char *s); + int get_length(); + void append(char *s); + void append(const MyString &obj); + ~MyString(); +}; + +// MyString.cpp +#include "MyString.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +MyString::MyString() { cout << "MyString类的默认构造函数被调用" << endl; } + +MyString::MyString(char *s) { + length = strlen(s); + str = new char[length + 1]; + str[length] = '\0'; + //安全拷贝 + strncpy(str, s, length + 1); + cout << "MyString类的有参构造函数被调用,当前字符串为:" << str << endl; +} +MyString::~MyString() { + cout << "MyString类的析构函数被调用,当前字符串为:" << str << endl; + delete str; +}; + +MyString::MyString(const MyString &obj) { + set_string(obj.str); + cout << "MyString类的复制构造函数被调用,当前字符串为:"; + cout << obj.str << endl; +} + +MyString &MyString::operator=(const MyString &obj) { + set_string(obj.str); + cout << "MyString类的赋值运算符被调用,当前字符串为:"; + cout << obj.str << endl; + return *this; +} + +char *MyString::get_string() { return str; } +void MyString::set_string(char *s) { + length = strlen(s); + str = new char[length + 1]; + str[length] = '\0'; + //安全拷贝 + strncpy(str, s, length + 1); +} +int MyString::get_length() { return length; } + +void MyString::append(char *s) { + length = length + strlen(s); + char *str_temp = new char[length + 1]; + str_temp[length] = '\0'; + strncpy(str_temp, str, length + 1); + strncat(str_temp, s, length + 1); + delete str; + str = str_temp; +} + +void MyString::append(const MyString &obj) { this->append(obj.str); } + +int main(int argc, char *argv[]) { + MyString str; + str.set_string("I love C++, "); + cout << "字符串长度:" << str.get_length() << "\t" << str.get_string() + << endl; + str.append("yeah!"); + cout << "字符串长度:" << str.get_length() << "\t" << str.get_string() + << endl; + { + MyString str("I like C++ programming!"); + MyString str2(str), str3 = str; + } + MyString str2; + cout << str.get_string() << endl; + str2 = str; + str2.append(str2); + cout << str2.get_string() << endl; + return 0; +} +``` + +得到的运行结果为: +![结果](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1112fw710c.png) + +### 组合类 {#组合类} +> 设计一个学生类,该类与MyString类构成组合类;重新设计学生列表类。 +> +> 对于学生(包含姓名、学号、成绩等),请完善封装学生类CStudent(将之前使用的结构体STUDENT封装为该类),其中学生的姓名、专业要使用上题的MyString类的对象存储;完善封装学生列表类CStudentList保存数量不定的学生,并在这个类中实现增加学生、按学号删除、修改学生信息、显示学生信息的功能,显示学生信息的函数要有重载版本;为CStudentList类提供复制构造函数、赋值运算符函数;注意初始化列表的使用。 + +该题是将字符串类和之前构造的学生类进行组合。这道题的关键在于CStudentList类中的拷贝构造函数和赋值运算符的重载,这两部分的代码如下 +```c++ + // CStudentList的拷贝构造函数 + CStudentList(const CStudentList &obj) { + for (auto stu : obj.students) { + CStudent *stu_temp = new CStudent(); + stu_temp->sno = stu.second->sno; + // 使用了MyString的拷贝构造函数 + stu_temp->name = stu.second->name; + stu_temp->score = stu.second->score; + stu_temp->specialty = stu.second->specialty; + students.insert(make_pair(stu.second->sno, stu_temp)); + } + } + // 赋值运算符函数 + CStudentList &operator=(const CStudentList &obj) { + for (auto stu : obj.students) { + CStudent *stu_temp = new CStudent(); + stu_temp->sno = stu.second->sno; + // 使用了MyString的拷贝构造函数 + stu_temp->name = stu.second->name; + stu_temp->score = stu.second->score; + stu_temp->specialty = stu.second->specialty; + students.insert(make_pair(stu.second->sno, stu_temp)); + this->students.insert(make_pair(stu.second->sno, stu_temp)); + } + return *this; + } +``` +剩余的部分主要在于用MyString类替代之前CStudent的char*,此时我们的MyString类已经可以通过'='进行直接赋值,非常方便,不再需要操作char*时的strcpy等函数。再用CStudentList来操作CStudent即可,这里在修改学生信息时,如果不存在该学生的信息则会报错,不能修改。代码如下 +```c++ +#include "MyString.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +MyString::MyString() { cout << "MyString类的默认构造函数被调用" << endl; } + +MyString::MyString(char *s) { + length = strlen(s); + str = new char[length + 1]; + str[length] = '\0'; + //安全拷贝 + strncpy(str, s, length + 1); + cout << "MyString类的有参构造函数被调用,当前字符串为:" << str << endl; +} +MyString::~MyString() { + cout << "MyString类的析构函数被调用,当前字符串为:" << str << endl; + delete str; +}; + +MyString::MyString(const MyString &obj) { + set_string(obj.str); + cout << "MyString类的复制构造函数被调用,当前字符串为:"; + cout << obj.str << endl; +} + +MyString &MyString::operator=(const MyString &obj) { + set_string(obj.str); + cout << "MyString类的赋值运算符被调用,当前字符串为:"; + cout << obj.str << endl; + return *this; +} + +char *MyString::get_string() { return str; } +void MyString::set_string(char *s) { + length = strlen(s); + str = new char[length + 1]; + str[length] = '\0'; + //安全拷贝 + strncpy(str, s, length + 1); +} +int MyString::get_length() { return length; } + +void MyString::append(char *s) { + length = length + strlen(s); + char *str_temp = new char[length + 1]; + str_temp[length] = '\0'; + strncpy(str_temp, str, length + 1); + strncat(str_temp, s, length + 1); + delete str; + str = str_temp; +} + +void MyString::append(const MyString &obj) { this->append(obj.str); } + +// CStudent类 +class CStudent { +public: + int sno; + MyString name; + MyString specialty; + int score; + CStudent(); + CStudent(CStudent &&) = default; + CStudent(const CStudent &) = default; + CStudent &operator=(CStudent &&) = default; + CStudent &operator=(const CStudent &) = default; + ~CStudent(); + +private: +}; + +class CStudentList { +public: + CStudentList(){}; + // CStudentList的拷贝构造函数 + CStudentList(const CStudentList &obj) { + for (auto stu : obj.students) { + CStudent *stu_temp = new CStudent(); + stu_temp->sno = stu.second->sno; + // 使用了MyString的拷贝构造函数 + stu_temp->name = stu.second->name; + stu_temp->score = stu.second->score; + stu_temp->specialty = stu.second->specialty; + students.insert(make_pair(stu.second->sno, stu_temp)); + } + } + // 赋值运算符函数 + CStudentList &operator=(const CStudentList &obj) { + for (auto stu : obj.students) { + CStudent *stu_temp = new CStudent(); + stu_temp->sno = stu.second->sno; + // 使用了MyString的拷贝构造函数 + stu_temp->name = stu.second->name; + stu_temp->score = stu.second->score; + stu_temp->specialty = stu.second->specialty; + students.insert(make_pair(stu.second->sno, stu_temp)); + this->students.insert(make_pair(stu.second->sno, stu_temp)); + } + return *this; + } + ~CStudentList(){}; + map students; + // 有默认形参,使用引用变量 + void add_stu(int sno, char *name, char *specialty, int score = 60) { + CStudent *student = new CStudent(); + student->sno = sno; + student->score = score; + student->name.set_string(name); + student->specialty.set_string(specialty); + students.insert(make_pair(sno, student)); + } + + void del_stu(int sno) { + // 释放内存 + delete (students.find(sno)->second); + students.erase(sno); + cout << "Delete success!"; + } + // 默认全部显示 + void look_up_stu() { + for (auto stu : students) { + cout << "Sno = " << stu.second->sno + << " Name = " << stu.second->name.get_string() + << " Specialty = " << stu.second->specialty.get_string() + << " Score = " << stu.second->score << endl; + } + } + + // 重载的显示函数 + void look_up_stu(int flag, int min = 0, int max = 100) { + for (auto stu : students) { + if ((stu.second->score) >= min && stu.second->score <= max) { + cout << "Sno = " << stu.second->sno + << " Name = " << stu.second->name.get_string() + << " Specialty = " << stu.second->specialty.get_string() + << " Score = " << stu.second->score << endl; + } + } + } + + // 根据学号修改学生信息 + void change_stu(int sno, char *name, char *specialty, int score = 60) { + int keycount = students.count(sno); + if (keycount != 1) { + cout << "Don't have this student, please add it to system!!" << endl; + return; + } else { + students.find(sno)->second->sno = sno; + students.find(sno)->second->name.set_string(name); + students.find(sno)->second->specialty.set_string(specialty); + students.find(sno)->second->score = score; + cout << "Update success!" << endl; + return; + } + } +}; + +int main() { + char select; + cout << "Welcome to my student manage system!!!!!" << endl; + CStudentList *stu_info = new CStudentList(); + while (true) { + cout << "\nPlease select the operation you want:" << endl + << "\t1.add a student.\n" + << "\t2.delete a student\n" + << "\t3.display students information\n" + << "\t4.change student information\n" + << "\tq for quit\n" + << "your select:"; + cin >> select; + switch (select) { + case '1': { + int number, average; + string aver; + string name, specialty; + cout << "The student number:"; + cin >> number; + cout << "The student name:"; + cin >> name; + cout << "The student specialty:"; + cin >> specialty; + cin.get(); + cout << "The student score(can be nothing):"; + getline(cin, aver); + if (aver.length() == 0) { + stu_info->add_stu(number, (char *)name.data(), + (char *)specialty.data()); + } else { + average = stoi(aver); + stu_info->add_stu(number, (char *)name.data(), (char *)specialty.data(), + average); + } + break; + } + case '2': { + int number; + cout << "The student number:"; + cin >> number; + stu_info->del_stu(number); + break; + } + case '3': { + string min, max; + cout << "Enter the min score of the :"; + cin.get(); + getline(cin, min); + cout << "Enter the max score of the :"; + getline(cin, max); + if (min.length() == 0 && max.length() == 0) { + stu_info->look_up_stu(); + break; + } else if (max.length() == 0) { + stu_info->look_up_stu(1, stoi(min)); + } else if (min.length() == 0) { + stu_info->look_up_stu(1, 0, stoi(max)); + } else { + stu_info->look_up_stu(1, stoi(min), stoi(max)); + } + break; + } + case '4': { + int number, average; + string aver; + string name, specialty; + cout << "The student number:"; + cin >> number; + cout << "The student name:"; + cin >> name; + cout << "The student specialty:"; + cin >> specialty; + cin.get(); + cout << "The student score(can be nothing):"; + getline(cin, aver); + if (aver.length() == 0) { + stu_info->change_stu(number, (char *)name.data(), + (char *)specialty.data()); + } else { + average = stoi(aver); + stu_info->change_stu(number, (char *)name.data(), + (char *)specialty.data(), average); + } + break; + } + case 'q': { + return 0; + } + default: + break; + } + } +} +``` + +运行的效果为 + +![img](https://gcore.jsdelivr.net/gh/game-loader/picbase@master/uPic/1112H6w9uJ.png)