声明
题解包含以下内容:
- (相对)高级的 C++ 模板及语法技巧
- 仅适用于 C++20 标准的代码
- 强烈的个人代码风格和 Modern C++ 范式追求
- 泛滥的标准库函数
换句话说就是(相较于其他公开题解)更低的查重率和更高的使用门槛;请酌情使用。
【id:159】【20分】A. 音像制品(类与对象)
题目描述
某商店出租音像制品,制品信息包括:类型、名称、租金单价、状态。
其中类型用单个数字表示,对应关系为:1-黑胶片,2-CD,3-VCD,4-DVD
名称是字符串,存储制品的名称信息
租金单价表示每天租金价格
状态用单个数字表示,0是未出租,1是已出租
商店提供业务操作包括
- 初始化(使用构造方法),从键盘输入音像制品的信息,并设置到对象中
- 查询print,输出音像制品的信息
- 计算租金fee,参数是租借的天数,输出租金总价,如果未出租则提示,具体输出信息看示范
请定义音像制品类,并创建相应的对象来完成操作
题目涉及的数值均用整数处理
输入
第一行输入n表示有n个音像制品
每个音像制品对应两行输入
一行输入一个音像制品的多个参数,具体为:类型、名称、租金单价(正整数)、状态
一行输入操作命令,如果输入为0表示查询操作,非0则表示查询并且计算租金费用,租用天数就是这个非0值
依次输入2n行
输出
根据每个音像制品的操作命令,调用相应的操作,然后输出相关结果
输出样式看示范。
样例
输入样例1 | 输出样例1 |
---|---|
4 1 AAA 43 1 0 2 BBB 19 0 3 3 CCC 27 1 5 4 DDD 32 1 7 | 黑胶片[AAA]已出租 CD[BBB]未出租 未产生租金 VCD[CCC]已出租 当前租金为135 DVD[DDD]已出租 当前租金为224 |
Answer
#include <bits/stdc++.h>
using namespace std;
enum class ProductType {
vinyl = 1, CD, VCD, DVD
};
istream& operator>>( istream& is, ProductType& pt )
{
size_t pos {}; is >> pos;
pt = static_cast<ProductType>( pos );
return is;
}
class Product {
ProductType type_;
string name_;
int price_;
int status_;
static string_view type_map( ProductType type ) {
switch ( type ) {
case ProductType::vinyl: return "黑胶片"sv;
case ProductType::CD: return "CD"sv;
case ProductType::VCD: return "VCD"sv;
case ProductType::DVD: return "DVD"sv;
default: assert( false );
}
}
public:
Product( ProductType type, string name, int price, int status )
: type_ { type }, name_ { move( name ) }, price_ { price }, status_ { status } {}
Product() : Product( ProductType::vinyl, "", {}, {} ) {}
void fee( uint64_t days ) {
cout << static_cast<string>(*this) << endl;
if ( days != 0 ) {
if ( status_ == 0 )
cout << "未产生租金" << endl;
else cout << "当前租金为" << price_ * days << endl;
}
}
operator string() const {
return string( type_map( type_ ) ) + "[" + name_ + "]" + ( status_ == 0 ? "未出租" : "已出租");
}
friend istream& operator>>( istream& is, Product& pt ) {
return is >> pt.type_ >> pt.name_ >> pt.price_ >> pt.status_;
}
};
int main()
{
#ifdef _WIN32
system( "chcp 65001" );
#endif
size_t n; cin >> n;
while ( n-- ) {
Product p; cin >> p;
int query; cin >> query;
p.fee( query );
}
}
【id:300】【20分】B. OOP双人决斗(多重继承)
题目描述
写一个Node2D基类,属性有位置location(String)
一个Body子类继承自Node2D,属性初始生命值maxHealth,当前生命值health,防御力defense
一个Weapon子类也继承自Node2D,属性有武器名w_name,武器伤害damage
一个Player多继承自Body和Weapon,属性有名字name,方法有attack,对目标造成伤害
在主函数创建两个Player,p1、p2,判断在p1首先开始攻击的情况下谁会获胜
我们规定,每次造成的伤害等于damage减去defense
输入
输入:地点location,玩家1的名字、生命值、防御力、武器名、武器伤害,玩家2的名字、生命值、防御力、武器名、武器伤害
输出
输出:获胜信息
样例
输入样例1 | 输出样例1 |
---|---|
palace p1 30 5 bow 30 p2 50 10 sword 20 | p1 deal 20 damage to p2 p2 still have 30 health p2 deal 15 damage to p1 p1 still have 15 health p1 deal 20 damage to p2 p2 still have 10 health p2 deal 15 damage to p1 p2 defeated p1 by sword in palace |
Answer
#include <bits/stdc++.h>
using namespace std;
class Node2D {
protected:
string location_;
public:
Node2D( string location ) : location_ { move( location ) } {}
virtual ~Node2D() = 0;
string_view location() const { return location_; }
};
Node2D::~Node2D() {}
class Body : virtual public Node2D {
protected:
int max_health_, health_, defense_;
public:
Body( string location, int max_health, int defense )
: Node2D( move( location ) ), max_health_ { max_health }
, health_ { max_health }, defense_ { defense } {}
int health() const { return health_; }
};
class Weapon : virtual public Node2D {
protected:
string w_name_;
int damage_;
public:
Weapon( string location, string w_name, int damage )
: Node2D( move( location ) ), w_name_ { move( w_name ) }, damage_ { damage } {}
string_view w_name() const { return w_name_; }
};
class Player : public Body, public Weapon {
string name_;
public:
Player( string location, int max_health, int defense, string w_name, int damage, string name )
: Node2D( move( location ) )
, Body( "", max_health, defense )
, Weapon( "", move( w_name ), damage )
, name_ { move( name ) } {}
Player( string location )
: Player( move( location ), {}, {}, {}, {}, {} ) {}
string_view name() const { return name_; }
int attack( Player& p2 ) {
const int p1_damage = damage_ - p2.defense_;
p2.health_ -= p1_damage;
return p1_damage;
}
friend istream& operator>>( istream& is, Player& p ) {
is >> p.name_ >> p.max_health_ >> p.defense_ >> p.w_name_ >> p.damage_;
p.health_ = p.max_health_;
return is;
}
};
ostream& operator<<( ostream& os, const Player& p )
{
return os << p.name() << " still have " << p.health() << " health\n\n";
}
bool foo( Player& p1, Player& p2 )
{
const auto damage = p1.attack( p2 );
cout << p1.name() << " deal " << damage << " damage to " << p2.name() << endl;
if ( p2.health() <= 0 ) {
cout << p1.name() << " defeated " << p2.name() << " by " << p1.w_name() << " in " << p1.location() << endl;
return true;
}
cout << p2;
return false;
}
int main()
{
string location; cin >> location;
auto p1 = Player( location );
auto p2 = Player( move( location ) );
cin >> p1 >> p2;
while ( true ) {
if ( foo( p1, p2 ) )
break;
else if ( foo( p2, p1 ) )
break;
}
}
【id:181】【20分】C. 宠物的生长(虚函数和多态)
题目描述
需要开发一个系统,对宠物的生长状态进行管理。给出下面的基类框架:
class Pet
{ protected:
string name;//姓名
float length;//身长
float weight;//体重
CDate current;//开始记录时间
(日期类CDate包含年、月、日三个私有数据,其他方法根据需要自拟。)
public:
virtual void display(CDate day)=0;//输出目标日期时宠物的身长和体重
}
以Pet为基类,构建出Cat和Dog两个类:
Cat一天身长加0.1,体重加0.2。
Dog一天身长加0.2,体重加0.1。
生成上述类并编写主函数,要求主函数中有一个基类指针Pet *pt,用于测试子类数据。
主函数根据输入的信息,相应建立Cat类对象或Dog类对象,并给出测量日期时宠物的身长和体重。
输入
第一行为测试次数
第二行是开始记录日期
从第三行起,每个测试用例占一行,每行给出宠物的基本信息:宠物的类型(1为Cat,2为Dog)、名字、身长、体重、最后测量的日期。
输出
要求输出目标日期宠物姓名、身长和体重(结果要求保留小数点后2位)。若测量日期小于开始日期,输出”error”。
样例
输入样例1 | 输出样例1 |
---|---|
3 2019 5 5 1 tony 10 10 2018 12 30 2 jerry 5 6 2019 5 10 1 tom 3 4 2019 6 1 | error jerry after 5 day: length=6.00,weight=6.50 tom after 27 day: length=5.70,weight=9.40 |
Answer
#include <bits/stdc++.h>
using namespace std;
class CDate {
int32_t year_;
int16_t month_, day_;
static constexpr int16_t base_year = 1970;
static constexpr std::array<int8_t, 12> months = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static bool is_leap( int year ) {
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
int count() const {// 返回当前日期是今年第几天
return accumulate( months.begin(), months.begin() + (month_ - 1), day_ ) + (month_ > 2 ? is_leap(year_) : 0);
}
public:
CDate( int32_t year, int16_t month, int16_t day )
: year_ { year }, month_ { month }, day_ { day } {}
CDate() : CDate( {}, {}, {} ) {}
/// @brief 返回两个日期之间差了多少天
int32_t operator-( const CDate& d ) const {
int32_t this_days = count(), d_days = d.count();
for ( int32_t i = base_year; i < year_; ++i )
this_days += 365 + is_leap( i );
for ( int32_t i = base_year; i < d.year_; ++i )
d_days += 365 + is_leap( i );
return this_days - d_days;
}
bool operator<( const CDate& d ) const {
if ( d.year_ == year_ )
if ( d.month_ == month_ )
return day_ < d.day_;
else return month_ < d.month_;
else return year_ < d.year_;
}
friend istream& operator>>( istream& is, CDate& d ) {
return is >> d.year_ >> d.month_ >> d.day_;
}
};
class Pet {
protected:
string name_;
float length_, weight_;
CDate beginning_;
public:
Pet( string name, float length, float weight, CDate date )
: name_ { move( name ) }, length_ { length }, weight_ { weight }, beginning_ { move( date ) } {}
virtual ~Pet() {}
virtual void display( CDate day ) = 0;
};
class Cat : public Pet {
public:
Cat( string name, float length, float weight, CDate date )
: Pet( move( name ), length, weight, move( date ) ) {}
Cat() : Cat( {}, {}, {}, {} ) {}
void display( CDate day ) override {
if ( day < beginning_ ) {
cout << "error" << endl;
return;
}
auto day_differ = abs( beginning_ - day );
cout << name_ << " after " << day_differ << " day: length="
<< fixed << setprecision( 2 ) << (length_ + day_differ * 0.1)
<< ",weight=" << (weight_ + day_differ * 0.2) << endl;
}
};
class Dog : public Pet {
public:
Dog( string name, float length, float weight, CDate date )
: Pet( move( name ), length, weight, move( date ) ) {}
Dog() : Dog( {}, {}, {}, {} ) {}
void display( CDate day ) override {
if ( day < beginning_ ) {
cout << "error" << endl;
return;
}
auto day_differ = abs( beginning_ - day );
cout << name_ << " after " << day_differ << " day: length="
<< fixed << setprecision( 2 ) << (length_ + day_differ * 0.2)
<< ",weight=" << (weight_ + day_differ * 0.1) << endl;
}
};
int main()
{
size_t t = 0; cin >> t;
CDate start; cin >> start;
Pet* animal = nullptr;
while ( t-- ) {
int type {}; cin >> type;
if ( type == 1 ) {
string name;
float length, weight;
cin >> name >> length >> weight;
animal = new Cat( move( name ), length, weight, start );
} else {
string name;
float length, weight;
cin >> name >> length >> weight;
animal = new Dog( move( name ), length, weight, start );
}
CDate last_day; cin >> last_day;
animal->display( last_day );
delete animal;
animal = nullptr;
}
}
【id:187】【20分】D. 集合(运算符重载)
题目描述
集合是由一个或多个确定的元素所构成的整体。集合的运算有并、交、相对补等。
集合A和集合B的交集:由属于A且属于B的相同元素组成的集合。
集合A和集合B的并集:由所有属于集合A或属于集合B的元素所组成的集合。
集合B关于集合A的相对补集,记做A-B:由属于A而不属于B的元素组成的集合。
假设集合A={10,20,30},集合B={1,10,50,8}。则A与B的并是{10,20,30,1,50,8},A与B的交是{10},B关于A的相对补集是{20,30}。
定义整数集合类CSet,属性包括:集合中的元素个数n,整型指针data存储集合中的元素。
方法有:
重载输出,按样例格式输出集合中的元素。
重载+运算符,求集合A和集合B的并集,并返回结果集合。
重载-运算符,求集合B关于集合A的相对补集,并返回结果集合。
重载*运算符,求集合A和集合B的交集,并返回结果集合。
主函数输入集合A、B的数据,计算集合的并、交、相对补。
可根据题目,为CSet类添加需要的成员函数。
输入
测试次数
每组测试数据两行,格式如下:
第一行:集合A的元素个数和元素
第二行:集合B的元素个数和元素
输出
每组测试数据输出如下:
第一行:集合A
第二行:集合B
第三行:A和B的并
第四行:A和B的交
第五行:B关于A的相对补集 与 A关于B的相对补集的并,即(A-B)+(B-A)
每组测试数据间以空行分隔。
样例
输入样例1 | 输出样例1 |
---|---|
2 3 10 20 30 4 10 1 2 3 5 100 2 3 4 -10 6 -34 12 2 4 90 100 | A:10 20 30 B:10 1 2 3 A+B:10 20 30 1 2 3 A*B:10 (A-B)+(B-A):20 30 1 2 3 A:100 2 3 4 -10 B:-34 12 2 4 90 100 A+B:100 2 3 4 -10 -34 12 90 A*B:100 2 4 (A-B)+(B-A):3 -10 -34 12 90 |
Answer
#include <bits/stdc++.h>
using namespace std;
class CSet {
size_t n_;
int* elements_;
public:
CSet() : n_ {}, elements_ { nullptr } {}
CSet( const CSet& lhs ) : CSet() {
n_ = lhs.n_;
elements_ = new int[n_];
memcpy( elements_, lhs.elements_, sizeof( int ) * n_ );
}
CSet( const vector<int>& lhs ) : CSet() {
n_ = lhs.size();
elements_ = new int[n_];
memcpy( elements_, lhs.data(), sizeof( int ) * n_ );
}
CSet( CSet&& rhs ) : CSet() {
swap( n_, rhs.n_ );
swap( elements_, rhs.elements_ );
}
~CSet() {
delete[] elements_;
elements_ = nullptr;
n_ = 0;
}
CSet& operator=( const CSet& lhs ) {
if ( this != &lhs ) {
delete[] elements_;
n_ = lhs.n_;
elements_ = new int[n_];
memcpy( elements_, lhs.elements_, sizeof( int ) * lhs.n_ );
}
return *this;
}
CSet& operator=( CSet&& rhs ) {
if ( this != &rhs ) {
swap( n_, rhs.n_ );
swap( elements_, rhs.elements_ );
}
return *this;
}
// 并集
CSet operator+( const CSet& lhs ) const {
set<int> std_set1( elements_, elements_ + n_ );
vector<int> tmp_ret;
copy( elements_, elements_ + n_, back_inserter( tmp_ret ) );
for_each( lhs.elements_, lhs.elements_ + lhs.n_,
[&std_set1, &tmp_ret]( int e ) {
if ( !std_set1.contains( e ) )
tmp_ret.push_back( e );
}
);
return CSet( tmp_ret );
}
// 差集
CSet operator-( const CSet& lhs ) const {
set<int> std_set2( lhs.elements_, lhs.elements_ + lhs.n_ );
vector<int> tmp_ret;
copy( elements_, elements_ + n_, back_inserter( tmp_ret ) );
tmp_ret.erase(
remove_if( tmp_ret.begin(), tmp_ret.end(),
[&std_set2]( int ele ) -> bool { return std_set2.contains( ele ); } ),
tmp_ret.end()
);
return CSet( tmp_ret );
}
// 交集
CSet operator*( const CSet& lhs ) const {
set<int> std_set2( lhs.elements_, lhs.elements_ + lhs.n_ );
vector<int> tmp_ret;
for_each( elements_, elements_ + n_,
[&std_set2, &tmp_ret]( int e ) {
if ( std_set2.contains( e ) )
tmp_ret.push_back( e );
}
);
return CSet( tmp_ret );
}
friend istream& operator>>( istream& is, CSet& cset ) {
delete[] cset.elements_;
is >> cset.n_;
cset.elements_ = new int[cset.n_];
for ( size_t i = 0; i < cset.n_; ++i )
is >> cset.elements_[i];
return is;
}
friend ostream& operator<<( ostream& os, const CSet& cset ) {
for ( size_t i = 0; i < cset.n_; ++i )
os << cset.elements_[i] << (i == cset.n_ - 1 ? "" : " ");
return os;
}
};
int main()
{
size_t t = 0; cin >> t;
while (t--) {
CSet A, B;
cin >> A >> B;
cout << "A:" << A << endl
<< "B:" << B << endl
<< "A+B:" << (A + B) << endl
<< "A*B:" << (A * B) << endl
<< "(A-B)+(B-A):" << ((A - B) + (B - A)) << endl;
if ( t != 0 )
cout << endl;
}
}
【id:192】【20分】E. 最贵的书(重载+友元+引用)
题目描述
定义CBook,属性包含书名(string),编者(string)、售价(double),出版社(string)。方法有:重载输入、输出。
定义友元函数find(CBook *book, int n, int &max1index,int &max2index)查找n本书中售价最高、次高的两本书,并通过引用返回其下标。若有相同售价最高、次高的两本书,按输入顺序输出第一本、第二本。
输入n,输入n本书的信息,调用上述友元函数,求价格最高的两本书下标,并按样例格式输出书信息。
输入
测试次数
每组测试数据格式如下:
n
n行书信息(书名,编者,售价,出版社)
输出
每组测试数据输出两行:
第一行:售价最高的书信息。
第二行:售价次高的书信息。
具体输出格式见样例,售价保留两位小数。书中间以空格分隔。
样例
输入样例1 | 输出样例1 |
---|---|
1 5 python从入门到精通,艾里克.马瑟斯,62.00,人民邮电出版社 Java并发编程实战,盖茨,54.5,机械工业出版社 Effective Java中文版,约书亚.布洛克,94,机械工业出版社 重构 改善既有代码的设计,马丁.福勒,122.6,人民邮电出版社 活用数据:驱动业务的数据分析实战,陈哲,61.4,电子工业出版社 | 重构 改善既有代码的设计 马丁.福勒 122.60 人民邮电出版社 Effective Java中文版 约书亚.布洛克 94.00 机械工业出版社 |
提示
读取‘,’前的字符用 getline进行输入,如下所示
#include <string>
string name;
getline(cin, name, ‘,’);
怎么这么多一堆中文输入的构式题目
Answer
#include <bits/stdc++.h>
using namespace std;
class CBook {
string name_, author_;
string publisher_;
double price_;
public:
CBook( string name, string author, string publisher, double price )
: name_ { move( name ) }, author_ { move( author ) }
, publisher_ { move( publisher ) }, price_ { price } {}
CBook() : CBook( {}, {}, {}, {} ) {}
auto operator<=>( const CBook& other ) const {
return price_ <=> other.price_;
}
friend void find( CBook* book, int n, int& max1index, int& max2index ) {
auto max_book = max_element( book, book + n + 1,
[]( CBook& bk1, CBook& bk2 ) -> bool {
return bk1.price_ < bk2.price_;
}
);
max1index = max_book - book;
double max2_price = 0;
for ( int i = 0; i < n; ++i ) {
if ( i == max1index )
continue;
if ( book[i] <= *max_book && book[i].price_ >= max2_price ) {
max2index = i;
max2_price = book[i].price_;
}
}
}
friend istream& operator>>( istream& is, CBook& book ) {
string line_input; getline( is, line_input );
regex pattern( "([^,]+)" );
vector<string> parse_result;
for ( auto i = sregex_iterator( line_input.begin(), line_input.end(), pattern ); i != sregex_iterator(); ++i )
parse_result.push_back( i->str() );
book.name_ = parse_result[0], book.author_ = parse_result[1];
book.price_ = stod( parse_result[2] ), book.publisher_ = parse_result[3];
return is;
}
friend ostream& operator<<( ostream& os, const CBook& book ) {
return os << book.name_ << '\n'
<< book.author_ << '\n'
<< fixed << setprecision( 2 ) << book.price_ << '\n'
<< book.publisher_ << endl;
}
};
int main()
{
size_t t; cin >> t;
while ( t-- ) {
size_t n {}; cin >> n;
cin.ignore( (numeric_limits<streamsize>::max)(), '\n' );
vector<CBook> book( n );
for ( auto& e : book )
cin >> e;
int max1index = -1, max2index = -1;
find( book.data(), n, max1index, max2index );
cout << book[max1index] << "\n" << book[max2index] << endl;
}
}