声明
题解包含以下内容:
- (相对)高级的 C++ 模板及语法技巧
- 仅适用于 C++20 标准的代码
- 强烈的个人代码风格和 Modern C++ 范式追求
- 泛滥的标准库函数
换句话说就是(相较于其他公开题解)更低的查重率和更高的使用门槛;请酌情使用。
【id:31】【20分】A. Point(类与构造)
题目描述
下面是一个平面上的点的类定义,请在类外实现它的所有方法,并生成点测试它。
输入
测试数据的组数 t
第一组测试数据点p1的x坐标 第一组测试数据点p1的y坐标 第一组测试数据点p2的x坐标 第一组测试数据点p2的y坐标
…
输出
输出p1到p2的距离,保留两位小数。详情参考输出样例。
在C++中,输出指定精度的参考代码如下:
#include <iostream>
#include <iomanip> //必须包含这个头文件
using namespace std;
void main( )
{ double a =3.14;
cout<<fixed<<setprecision(3)<<a<<endl; //输出小数点后3位
}
样例
输入样例1 | 输出样例1 |
---|---|
2 1 2 3 4 -1 0.5 -2 5 | Distance of Point(1.00,2.00) to Point(3.00,4.00) is 2.83 Distance of Point(-1.00,0.50) to Point(-2.00,5.00) is 4.61 |
Answer
#include <bits/stdc++.h>
using namespace std;
class Point {
double x, y;
public:
Point();
Point( double x_, double y_ );
double getX() const;
double getY() const;
void setX( double x_ ) noexcept;
void setY( double y_ ) noexcept;
double distanceToAnotherPoint( const Point& pt ) const;
friend istream& operator>>( istream& is, Point& pt ) {
return is >> pt.x >> pt.y;
}
};
Point::Point() : Point({}, {}) {}
Point::Point( double x_, double y_ )
: x {x_}, y {y_} {}
double Point::getX() const { return x; }
double Point::getY() const { return y; }
void Point::setX( double x_ ) noexcept { x = x_; }
void Point::setY( double y_ ) noexcept { y = y_; }
double Point::distanceToAnotherPoint( const Point& pt ) const
{
return sqrt( pow( abs( x - pt.x ), 2 ) + pow( abs( y - pt.y ), 2 ) );
}
ostream& operator<<( ostream& os, const Point& pt )
{
return os << fixed << setprecision(2) << '(' << pt.getX() << ',' << pt.getY() << ')';
}
int main()
{
size_t t = 0;
cin >> t;
while (t--) {
Point pt1, pt2;
cin >> pt1 >> pt2;
cout << "Distance of Point"sv << pt1
<< " to Point"sv << pt2
<< " is "sv << pt1.distanceToAnotherPoint( pt2 ) << endl;
}
return 0;
}
【id:32】【20分】B. Date(类与构造)
题目描述
下面是一个日期类的定义,请在类外实现其所有的方法,并在主函数中生成对象测试之。
注意,在判断明天日期时,要加入跨月、跨年、闰年的判断
例如9.月30日的明天是10月1日,12月31日的明天是第二年的1月1日
2月28日的明天要区分是否闰年,闰年则是2月29日,非闰年则是3月1日
输入
测试数据的组数t
第一组测试数据的年 月 日
…
要求第一个日期的年月日初始化采用构造函数,第二个日期的年月日初始化采用setDate方法,第三个日期又采用构造函数,第四个日期又采用setDate方法,以此类推。
输出
输出今天的日期
输出明天的日期
样例
输入样例1 | 输出样例1 |
---|---|
4 2012 1 3 2012 2 28 2012 3 31 2012 4 30 | Today is 2012/01/03 Tomorrow is 2012/01/04 Today is 2012/02/28 Tomorrow is 2012/02/29 Today is 2012/03/31 Tomorrow is 2012/04/01 Today is 2012/04/30 Tomorrow is 2012/05/01 |
提示
C++中设置填充字符的代码参考如下:
cout << setfill(‘0’) << setw(2) << month; //设置宽度为2,前面补’0’
需要头文件#include <iomanip>
Answer
#include <bits/stdc++.h>
using namespace std;
class Date {
int year, month, day;
static constexpr bitset<12> big_month = 0b10'10'11'01'01'01;
public:
Date();
Date( int yy, int mm, int dd );
int getYear() const;
int getMonth() const;
int getDay() const;
void setDate( int yy, int mm, int dd ) noexcept;
void print() const;
void addOneDay() noexcept;
friend istream& operator>>( istream& is, Date& lhs ) {
return is >> lhs.year >> lhs.month >> lhs.day;
}
};
Date::Date() : Date({}, {}, {}) {}
Date::Date( int yy, int mm, int dd )
: year { yy }, month { mm }, day { dd } {}
int Date::getYear() const { return year; }
int Date::getMonth() const { return month; }
int Date::getDay() const { return day; }
void Date::print() const {
cout << getYear() << '/' <<
setfill( '0' ) << setw( 2 ) << getMonth() << '/' <<
setfill('0') << setw(2) << getDay();
}
void Date::setDate( int yy, int mm, int dd ) noexcept
{
year = yy; month = mm; day = dd;
}
void Date::addOneDay() noexcept {
day += 1;
if ( const int day_upper =
(big_month[month - 1] ? 31
: (month != 2 ? 30
: ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? 29
: 28));
day > day_upper ) {
++month;
day -= day_upper;
}
if ( month >= 12 ) {
++year;
month -= 12;
}
}
ostream& operator<<( ostream& os, const Date& lhs )
{
return os << lhs.getYear() << '/' <<
setfill( '0' ) << setw( 2 ) << lhs.getMonth() << '/' <<
setfill( '0' ) << setw( 2 ) << lhs.getDay();
}
int main()
{
size_t t = 0;
cin >> t;
for ( size_t i = 0; i < t; ++i ) {
Date dt; cin >> dt;
cout << "Today is " << dt << endl;
dt.addOneDay();
cout << "Tomorrow is " << dt << endl;
}
}
【id:33】【20分】C. 分数类(类与构造)
题目描述
完成下列分数类的实现:
class CFraction
{
private:
int fz, fm;
public:
CFraction(int fz_val, int fm_val) ;
CFraction add(const CFraction &r);
CFraction sub(const CFraction &r);
CFraction mul(const CFraction &r);
CFraction div(const CFraction &r);
int getGCD(); // 求对象的分子和分母的最大公约数
void print();
};
求两数a、b的最大公约数可采用辗转相除法,又称欧几里得算法,其步骤为:
- 交换a, b使a > b;
- 用a除b得到余数r,若r=0,则b为最大公约数,退出.
- 若r不为0,则用b代替a, r代替b,此时a,b都比上一次的小,问题规模缩小了;
- 继续第2步。
注意:如果分母是1的话,也按“分子/1”的方式输出。
输入
测试数据的组数 t
第一组第一个分数
第一组第二个分数
第二组第一个分数
第二组第二个分数
…
输出
第一组两个分数的和
第一组两个分数的差
第一组两个分数的积
第一组两个分数的商
第二组两个分数的和
第二组两个分数的差
第二组两个分数的积
第二组两个分数的商
…
样例
输入样例1 | 输出样例1 |
---|---|
3 1/2 2/3 3/4 5/8 21/23 8/13 | 7/6 -1/6 1/3 3/4 11/8 1/8 15/32 6/5 457/299 89/299 168/299 273/184 |
Answer
#include <bits/stdc++.h>
using namespace std;
class CFraction {
int fz, fm;
void reduction() {
if ( int gcd_num = gcd( abs( fz ), abs( fm ) );
gcd_num >= 1 ) {
fz /= gcd_num;
fm /= gcd_num;
}
// 保持负号在分子上
if ( fm < 0 && fz > 0 ) {
fz *= -1;
fm = abs(fm);
} else if ( fz < 0 && fm < 0 ) {
fz = abs( fz );
fm = abs( fm );
}
}
public:
CFraction( int fz_val, int fm_val )
: fz { fz_val }, fm { fm_val } {
reduction();
}
CFraction() : CFraction( {}, {} ) {}
CFraction add( const CFraction& r ) {
//int lcm = fm == r.fm ? 1 : fm * r.fm / gcd( fm, r.fm );
return CFraction(fz * r.fm + r.fz * fm, fm * r.fm);
}
CFraction sub( const CFraction& r ) {
//int lcm = fm == r.fm ? 1 : fm * r.fm / gcd(fm, r.fm);
return CFraction(fz * r.fm - r.fz * fm, fm * r.fm);
}
CFraction mul( const CFraction& r ) {
return CFraction(fz * r.fz, fm * r.fm);
}
CFraction div( const CFraction& r ) {
return CFraction( fz * r.fm, fm * r.fz);
}
int getGCD() {
return gcd( fz, fm );
} // 求对象的分子和分母的最大公约数
void print() {
cout << fz << '/' << fm;
}
friend ostream& operator<<( ostream& os, const CFraction& lhs ) {
return (lhs.fz != 0 ? (os << lhs.fz << '/' << lhs.fm) : (os << 0));
}
friend istream& operator>>( istream& is, CFraction& lhs ) {
char tmp; return is >> lhs.fz >> tmp >> lhs.fm;
}
};
int main() {
int t; cin >> t;
while ( t-- ) {
CFraction num1, num2;
cin >> num1 >> num2;
cout << num1.add( num2 ) << endl
<< num1.sub( num2 ) << endl
<< num1.mul( num2 ) << endl
<< num1.div( num2 ) << endl << endl;
}
}
【id:34】【20分】D. Point_Array(类+构造+对象数组)
题目描述
上面是我们曾经练习过的一个习题,请在原来代码的基础上作以下修改:
1、增加自写的析构函数;
2、将getDisTo方法的参数修改为getDisTo(const Point &p);
3、根据输出的内容修改相应的构造函数。
然后在主函数中根据用户输入的数目建立Point数组,求出数组内距离最大的两个点之间的距离值。
输入
测试数据的组数 t
第一组点的个数
第一个点的 x 坐标 y坐标
第二个点的 x坐标 y坐标
…
输出
输出每组中距离最大的两个点以及其距离(存在多个距离都是最大值的情况下,输出下标排序最前的点组合。比如如果p[0]和p[9]、p[4]和p[5]之间的距离都是最大值,那么前一个是答案,因为p[0]排序最前)
…
在C++中,输出指定精度的参考代码如下:
#include <iostream>
#include <iomanip> //必须包含这个头文件
using namespace std;
void main( )
{ double a =3.141596;
cout<<fixed<<setprecision(3)<<a<<endl; //输出小数点后3位
样例
输入样例1 | 输出样例1 |
---|---|
2 4 0 0 5 0 5 5 2 10 3 -1 -8 0 9 5 0 | Constructor. Constructor. Constructor. Constructor. The longest distance is 10.44,between p[1] and p[3]. Distructor. Distructor. Distructor. Distructor. Constructor. Constructor. Constructor. Constructor. The longest distance is 17.03,between p[0] and p[1]. Distructor. Distructor. Distructor. Distructor. |
Answer
#include <bits/stdc++.h>
using namespace std;
class Point {
double x, y;
public:
Point();
Point( double x_, double y_ );
~Point();
double getX() const;
double getY() const;
void setX( double x_ ) noexcept;
void setY( double y_ ) noexcept;
double getDisTo( const Point& pt ) const;
friend istream& operator>>( istream& is, Point& pt ) {
return is >> pt.x >> pt.y;
}
};
Point::Point() : Point({}, {}) {}
Point::Point( double x_, double y_ )
: x { x_ }, y { y_ } {
cout << "Constructor."sv << endl;
}
Point::~Point() {
cout << "Distructor."sv << endl;
}
double Point::getX() const { return x; }
double Point::getY() const { return y; }
void Point::setX( double x_ ) noexcept { x = x_; }
void Point::setY( double y_ ) noexcept { y = y_; }
double Point::getDisTo( const Point& pt ) const
{
return sqrt( pow( abs( x - pt.x ), 2 ) + pow( abs( y - pt.y ), 2 ) );
}
int main()
{
size_t t; cin >> t;
while ( t-- ) {
size_t scale = 0; cin >> scale;
auto arr = vector<Point>( scale );
for ( auto& e : arr )
cin >> e;
double dist {};
size_t px {}, py {};
for ( size_t i = 0; i < scale; ++i ) {
for ( size_t ii = 0; ii < scale; ++ii ) {
if (ii == i) continue;
if ( const double distan = arr[i].getDisTo( arr[ii] );
distan > dist ) {
dist = distan;
px = i; py = ii;
}
}
}
cout << "The longest distance is "
<< fixed << setprecision( 2 ) << dist
<< ",between p[" << px << "] and p[" << py << "]." << endl;
}
}
【id:35】【20分】E. Stack(类与构造)
题目描述
上面是栈类的定义,栈是一种具有先进后出特点的线性表,请根据注释,完成类中所有方法的实现,并在主函数中测试之。
堆栈类的说明如下:
- 堆栈的数据实际上是保存在数组a中,而a开始是一个指针,在初始化时,根据实际需求将a动态创建为数组,数组长度根据构造函数的参数决定。
- size实际上就是数组的长度,当使用无参构造则size为10,当使用有参构造则size为s、
- top表示数组下标,也表示数组中下一个存放数据的空白位置。
- push操作表示堆栈的数组存放一个数据,例如一开始数组为空,则top为0,当有数据要入栈时,把数据存放在a[top]的位置,然后top加1指向下一个空白位置、数据进栈只能从栈顶进。
- pop操作表示一个数据要出栈,数据出栈只能从栈顶出,先把top减1指向栈顶数据,然后把数据返回。
- 判断堆栈空的条件是top是否等于0,判断堆栈满的条件是top是否等于size
输入
测试数据的组数 t
第一个栈的大小
第一个栈的元素列表,将该列表的元素依次进栈
…
输出
将栈元素依次出栈
样例
输入样例1 | 输出样例1 |
---|---|
2 5 1 2 3 4 5 7 -1 2 8 0 -3 1 3 | Constructor. 5 4 3 2 1 Destructor. Constructor. 3 1 -3 0 8 2 -1 Destructor. |
Answer
#include <bits/stdc++.h>
using namespace std;
class CStack {
int* a;
int size;
int top;
public:
CStack() : a { nullptr }, size {}, top { -1 } {
cout << "Constructor." << endl;
}
CStack( int s ) : CStack() {
size = s;
a = new int[size] {};
top = -1;
}
int get( int index ) {
return a[index];
}
void push( int n ) {
a[++top] = n;
}
int isEmpty() {
return top == -1;
}
int isFull() {
return top == size - 1;
}
int pop() {
return a[top--];
}
~CStack() {
delete[] a;
cout << "Destructor." << endl;
}
};
int main()
{
size_t t = 0;
cin >> t;
while ( t-- ) {
int stk_size; cin >> stk_size;
auto mystk = CStack( stk_size );
for ( size_t i = 0; i < stk_size; ++i ) {
int tmp; cin >> tmp;
mystk.push( tmp );
}
for ( int i = 0; i < stk_size; ++i ) {
cout << mystk.pop();
cout << (i < stk_size - 1 ? ' ' : '\n');
}
}
}