声明
题解包含以下内容:
- (相对)高级的 C++ 模板及语法技巧
- 仅适用于 C++20 标准的代码
- 强烈的个人代码风格和 Modern C++ 范式追求
- 泛滥的标准库函数
换句话说就是(相较于其他公开题解)更低的查重率和更高的使用门槛;请酌情使用。
【id:64】【20分】A. 图形面积(虚函数与多态)
题目描述
编写一个程序,定义抽象基类Shape,在Shape类中定义虚函数area();由它派生出3个派生类:Circle(圆形)、Square(正方形)、Rectangle(矩形)。用虚函数分别计算几种图形面积。
1、要求输出结果保留两位小数。
2、要求用基类指针数组,使它每一个元素指向每一个派生类对象。
输入
测试数据的组数 t
第一组测试数据中圆的半径
第一组测测试数据中正方形的边长
第一组测试数据中矩形的长、宽
…
第 t 组测试数据中圆的半径
第 t 组测测试数据中正方形的边长
第 t 组测试数据中矩形的长、宽
输出
第一组数据中圆的面积
第一组数据中正方形的面积
第一组数据中矩形的面积
…
第 t 组数据中圆的面积
第 t 组数据中正方形的面积
第 t 组数据中矩形的面积
样例
输入样例1 | 输出样例1 |
---|---|
2 1.2 2.3 1.2 2.3 2.1 3.2 1.23 2.12 | 4.52 5.29 2.76 13.85 10.24 2.61 |
Answer
#include <bits/stdc++.h>
using namespace std;
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() {}
};
class Circle : public Shape {
double radius;
public:
Circle( double rr ) : radius { rr } {}
virtual double area() const { return 3.14 * radius * radius; }
};
class Square : public Shape {
double side;
public:
Square( double ss ) : side { ss } {}
virtual double area() const { return side * side; }
};
class Rectangle : public Shape {
double length, width;
public:
Rectangle( double ll, double ww ) : length { ll }, width {ww} {}
virtual double area() const { return length * width; }
};
int main()
{
size_t t = 0; cin >> t;
while ( t-- ) {
vector<unique_ptr<Shape>> shapes( 3 );
double ll {}, rr {}, ww {};
cin >> rr; shapes[0] = make_unique<Circle>( rr );
cin >> ll; shapes[1] = make_unique<Square>( ll );
cin >> ll >> ww; shapes[2] = make_unique<Rectangle>( ll, ww );
cout << fixed << setprecision( 2 ) << shapes[0]->area() << endl
<< fixed << setprecision( 2 ) << shapes[1]->area() << endl
<< fixed << setprecision( 2 ) << shapes[2]->area() << endl;
}
}
【id:180】【20分】B. 汽车收费(虚函数和多态)
题目描述
现在要开发一个系统,实现对多种汽车的收费工作。 汽车基类框架如下所示:
class Vehicle
{
protected:
string no; //编号
public:
virtual void display()=0; //应收费用
}
以Vehicle为基类,构建出Car、Truck和Bus三个类。
Car的收费公式为: 载客数8+重量2
Truck的收费公式为:重量*5
Bus的收费公式为: 载客数*30
生成上述类并编写主函数,要求主函数中有一个基类指针Vehicle *pv;用来做测试用。
主函数根据输入的信息,相应建立Car,Truck或Bus类对象,对于Car给出载客数和重量,Truck给出重量,Bus给出载客数。假设载客数和重量均为整数。
输入
第一行表示测试次数。从第二行开始,每个测试用例占一行,每行数据意义如下:汽车类型(1为car,2为Truck,3为Bus)、编号、基本信息(Car是载客数和重量,Truck给出重量,Bus给出载客数)。
输出
车的编号、应缴费用
样例
输入样例1 | 输出样例1 |
---|---|
4 1 002 20 5 3 009 30 2 003 50 1 010 17 6 | 002 170 009 900 003 250 010 148 |
Answer
#include <bits/stdc++.h>
using namespace std;
class Vehicle {
protected:
string no;
public:
Vehicle( string _no ) : no { move( _no ) } {}
virtual ~Vehicle() {}
virtual void display() const = 0;
};
class Car : public Vehicle {
int num_load, weight;
public:
Car( string _no, int n, int w )
: Vehicle( move( _no ) ), num_load { n }, weight { w } {}
virtual void display() const {
cout << no << " " << (num_load * 8 + (weight << 1)) << endl;
}
};
class Truck : public Vehicle {
int weight;
public:
Truck( string _no, int w ) : Vehicle( move( _no ) ), weight { w } {}
virtual void display() const {
cout << no << " " << (weight * 5) << endl;
}
};
class Bus : public Vehicle {
int num_load;
public:
Bus( string _no, int n ) : Vehicle( move( _no ) ), num_load { n } {}
virtual void display() const {
cout << no << " " << (num_load * 30) << endl;
}
};
int main()
{
size_t t = 0; cin >> t;
Vehicle *pv = nullptr;
while ( t-- ) {
int order {}; cin >> order;
string no; cin >> no;
if ( order == 1 ) {
int n, w;
cin >> n >> w;
pv = new Car { move( no ), n, w };
} else if ( order == 2 ) {
int w;
cin >> w;
pv = new Truck { move( no ), w };
} else {
int n;
cin >> n;
pv = new Bus { move( no ), n };
}
pv->display();
delete pv;
}
}
【id:65】【20分】C. 支票账户(虚函数与多态)
题目描述
某银行的支票账户分为两类,一类为基本支票账户BaseAccount,另一类为具有透支保护特性的BasePlus支票账户。
BaseAccount支票账户的信息包括:客户姓名(name)、账户(account)、当前结余(balance);BaseAccount支票账户可以执行的操作包括:存款(deposit)、取款(withdraw)、显示账户信息(display)。注意:取款金额不能透支,否则显示出错信息“insufficient”。
BasePlus支票账户除包含BaseAccount的所有信息外,还包括以下信息:透支上限(默认为5000),当前可透支额度(limitSum);BasePlus支票账户可执行的操作与BaseAccount相同,但有两种操作的实现不同:(1)对于取款操作,可以在透支上限范围内透支,超过则显示出错信息“insufficient”;(2)对于显示操作,必须显示BasePlus的其他信息。
请实现BaseAccount类和BasePlus类,其中BasePlus类继承于BaseAccount类,注意BaseAccount账户名称以BA开头,BasePlus账户名称以BP开头。
要求只使用一个基类指针,指向所建立的对象,然后使用指针调用类中的方法。
输入
测试案例组数 t
第一组测试数据:
第一行输入账户信息:姓名 帐号 当前余额
第二行输入四个整数,表示对账户按顺序存款、取款、存款、取款
第二组测试数据:
…
输出
输出BaseAccount的信息
输出BasePlus的信息
样例
输入样例1 | 输出样例1 |
---|---|
4 Tom BA008 1000 1000 2000 1000 1200 Bob BP009 1000 1000 2000 1000 7000 May BA001 2000 500 1000 500 1000 Lily BP002 1000 500 2000 500 3000 | insufficient Tom BA008 Balance:1000 insufficient Bob BP009 Balance:1000 limit:5000 May BA001 Balance:1000 Lily BP002 Balance:0 limit:2000 |
Answer
#include <bits/stdc++.h>
using namespace std;
class BaseAccount {
protected:
string name, account;
double balance;
public:
BaseAccount( string nm, string acc, double bal )
: name { move( nm ) }, account { move( acc ) }, balance { bal } {}
virtual ~BaseAccount() {}
virtual void deposit( double amount ) {
if ( amount < 0 )
return;
balance += amount;
}
virtual bool withdraw( double amount ) {
if ( balance >= amount ) {
balance -= amount;
return true;
}
return false;
}
virtual void display() const {
cout << name << " " << account << " Balance:" << fixed << setprecision( 0 ) << balance << endl;
}
};
class BasePlus : public BaseAccount {
double overdraft, limit;
public:
BasePlus( string nm, string acc, double bal, double lim = 5000 )
: BaseAccount( move( nm ), move( acc ), bal ), overdraft { lim }, limit { lim } {}
virtual ~BasePlus() {}
virtual void deposit( double amount ) {
if ( amount < 0 )
return;
if ( limit < overdraft ) { // 要还钱
if ( overdraft - limit > amount ) { // 不够还
limit += amount;
amount = 0;
} else { // 够还
amount -= overdraft - limit;
limit = overdraft;
}
}
balance += amount;
}
virtual bool withdraw( double amount ) {
if ( balance + limit >= amount ) { // 够取
if ( balance - amount < 0 ) { // 触发透支
amount -= balance;
balance = 0;
} else { // 余额足够
balance -= amount;
amount = 0;
}
limit -= amount;
return true;
}
return false;
}
virtual void display() const {
cout << name << " " << account << " Balance:" << fixed << setprecision( 0 ) << balance << " limit:" << limit << endl;
}
};
int main()
{
size_t t = 0; cin >> t;
while ( t-- ) {
BaseAccount *ptr = nullptr;
string name, account; double balance {};
cin >> name >> account >> balance;
if ( account.find( "BA" ) != string::npos )
ptr = new BaseAccount( move( name ), move( account ), balance );
else
ptr = new BasePlus( move( name ), move( account ), balance );
for ( int _ = 0; _ < 2; ++_ ) {
double save {}, fetch {};
cin >> save >> fetch;
ptr->deposit( save );
if ( !ptr->withdraw( fetch ) )
cout << "insufficient\n";
}
ptr->display();
delete ptr;
}
}
【id:66】【20分】D. 动物园(虚函数与多态)
题目描述
某个动物园内,有老虎、狗、鸭子和猪等动物,动物园的管理员为每个动物都起了一个名字,并且每个动物都有年龄、体重等信息。每到喂食的时候,不同的动物都会叫唤(speak)。每种动物的叫唤声均不同,老虎的叫唤声是“AOOO”,狗的叫唤声是“WangWang”,鸭子的叫唤声是“GAGA”,猪的叫唤声是“HENGHENG”。
定义一个Animal的基类,Animal类有函数Speak(),并派生老虎、狗、鸭子和猪类,其能发出不同的叫唤声(用文本信息输出叫声)。
编写程序,输入动物名称、名字、年龄,让动物园内的各种动物叫唤。
要求:只使用一个基类指针,指向生成的对象并调用方法。
输入
测试案例的个数
第一种动物的名称 名字 年龄
第二种动物的名称 名字 年龄
…
输出
输出相应动物的信息
如果不存在该种动物,输出There is no 动物名称 in our Zoo. ,具体输出参考样例输出
样例
输入样例1 | 输出样例1 |
---|---|
4 Tiger Jumpjump 10 Pig Piglet 4 Rabbit labi 3 Duck tanglaoya 8 | Hello,I am Jumpjump,AOOO. Hello,I am Piglet,HENGHENG. There is no Rabbit in our Zoo. Hello,I am tanglaoya,GAGA. |
Answer
#include <bits/stdc++.h>
using namespace std;
class Animal {
string name, species;
int age;
public:
Animal( string nm, string spe, int ag )
: name { move( nm ) }, species { move( spe ) }, age { ag } {}
virtual ~Animal() {}
virtual void Speak() const = 0;
void display() const {
cout << "Hello,I am " << name << ",";
}
};
class Dog : public Animal {
public:
Dog( string nm, string spe, int ag ) : Animal( move( nm ), move( spe ), ag ) {}
void Speak() const override { cout << "WangWang.\n"; }
};
class Duck : public Animal {
public:
Duck( string nm, string spe, int ag ) : Animal( move( nm ), move( spe ), ag ) {}
void Speak() const override { cout << "GAGA.\n"; }
};
class Pig : public Animal {
public:
Pig( string nm, string spe, int ag ) : Animal( move( nm ), move( spe ), ag ) {}
void Speak() const override { cout << "HENGHENG.\n"; }
};
class Tiger : public Animal {
public:
Tiger( string nm, string spe, int ag ) : Animal( move( nm ), move( spe ), ag ) {}
void Speak() const override { cout << "AOOO.\n"; }
};
const unordered_set<string_view> zoo { "Dog"sv, "Duck"sv, "Pig"sv, "Tiger"sv };
int main()
{
size_t t = 0; cin >> t;
Animal *ani = nullptr;
while ( t-- ) {
string name, species; int age {};
cin >> species >> name >> age;
if ( !zoo.contains( species ) ) {
cout << "There is no " << species << " in our Zoo.\n";
continue;
}
if ( species == "Dog"sv )
ani = new Dog( move( name ), move( species ), age );
else if ( species == "Duck"sv )
ani = new Duck( move( name ), move( species ), age );
else if ( species == "Pig"sv )
ani = new Pig( move( name ), move( species ), age );
else ani = new Tiger( move( name ), move( species ), age );
ani->display(); ani->Speak();
delete ani; ani = nullptr;
}
}
【id:295】【20分】E. 进位与借位(虚函数和多态)
题目描述
某小学二年级的数学老师在教学生整数加减法运算时发现:班上的同学可以分成三类,第一类可以正确地完成加减法运算(GroupA);第二类可以正确地完成加法运算,但对于减法运算来说,总是忘记借位的处理(GroupB);第三类总是忘记加法的进位,也总是忘记减法的借位(GroupC)。(提示:小学二年级还没学负数。)
现在请模拟当老师在课堂提问某位同学时,同学会给出的回答。
实现时请基于下面的基类框架:
class Group
{
public:
virtual int add(int x, int y) = 0; // 输出加法的运算结果
virtual int sub(int x, int y) = 0; // 输出减法的运算结果
}
构建出GroupA, GroupB和GroupC三个派生类:
并编写主函数,要求主函数中有一个基类Group指针,通过该指针统一地进行add和sub运算。
输入
第一行表示测试次数。从第二行开始,每个测试用例占一行,每行数据意义如下:学生类别(1为第一类学生,2为第二类学生,3为第三类学生)、第一个数、第二个数。
输出
运算后的结果
样例
输入样例1 | 输出样例1 |
---|---|
3 1 79+81 2 81-79 3 183+69 | 160 12 142 |
Answer
#include <bits/stdc++.h>
using namespace std;
class Group {
public:
virtual ~Group() {}
virtual int add( int x, int y ) const = 0;
virtual int sub( int x, int y ) const = 0;
};
class GroupA : public Group {
public:
virtual int add( int x, int y ) const {
return x + y;
}
virtual int sub( int x, int y ) const {
return x - y;
}
};
class GroupB : public GroupA {
public:
int sub( int x, int y ) const override {
string x_str = to_string( x ), y_str = to_string( y );
int len = min( x_str.size(), y_str.size() );
string sum;
for ( int i = 0; i < len; ++i ) {
auto result = (x_str[x_str.size() - i - 1] -
y_str[y_str.size() - i - 1]);
result = result < 0 ? result + 10 : result;
sum.push_back( result + '0' );
}
sum.append( x_str.size() > y_str.size() ?
x_str.substr( 0, x_str.size() - len ) :
y_str.substr( 0, y_str.size() - len ) );
reverse( sum.begin(), sum.end() );
return stoi( sum );
}
};
class GroupC : public GroupB {
public:
int add( int x, int y ) const override {
string x_str = to_string( x ), y_str = to_string( y );
int len = min( x_str.size(), y_str.size() );
string sum;
for ( int i = 0; i < len; ++i )
sum.push_back(
((x_str[x_str.size() - i - 1] +
y_str[y_str.size() - i - 1] - ('0' * 2)) % 10) +
'0'
);
sum.append( x_str.size() > y_str.size() ?
x_str.substr( 0, x_str.size() - len ) :
y_str.substr( 0, y_str.size() - len ) );
reverse( sum.begin(), sum.end() );
return stoi( sum );
}
};
int main()
{
size_t t = 0; cin >> t;
while ( t-- ) {
Group *g = nullptr;
int order {}; cin >> order;
if ( order == 1 )
g = new GroupA();
else if ( order == 2 )
g = new GroupB();
else g = new GroupC();
std::smatch matches;
string expr; getline( cin, expr );
std::regex_search( expr, matches, std::regex { R"((\d+)([\+\-\*\/])(\d+))" } );
if ( matches[2] == '+' )
cout << g->add( stoi( matches[1] ), stoi( matches[3] ) ) << endl;
else cout << g->sub( stoi( matches[1] ), stoi( matches[3] ) ) << endl;
delete g; g = nullptr;
}
}