继续和MATRIX斗智斗勇!

//array.cpp
#include "array.h"
#include <cstdlib>

array::array()
{
    size = 0;
    data = nullptr;
}

array::array(int size, int val)
{
    this->size = size;
    this->data = new int[size];
    for (int i = 0; i < size; ++i)
    {
        this->data[i] = val;
    }
}

array::~array()
{
    if (data)
        delete[] data;
}

bool array::assign(int pos, int val)
{
    if (pos >= 0 && pos < size)
    {
        data[pos] = val;
        return true;
    }
    else
    {
        return false;
    }
}

void array::deep_copy(const array &another)
{
    if (this->data)
    {
        delete[] this->data;
    }

    this->size = another.size;
    this->data = new int[this->size];
    for (int i = 0; i < this->size; ++i)
    {
        this->data[i] = another.data[i];
    }
}//与前一题相同

array2d::array2d(int row, int col, int val)
{
    this->row = row;
    this->col = col;

    data = new array[row];
    
    array every_col = array(col, val);
    for (int i = 0; i < row; ++i)
    {
        data[i].deep_copy(every_col);
    }
}//构造函数要考虑到每一个成员的初始化

array2d::~array2d()
{
    if (data)
        delete[] data;
}

bool array2d::assign(int i, int j, int val)
{
    if (i >= 0 && i < row)
    {
        return data[i].assign(j, val);
    }
    else
    {
        return false;
    }
}

void array2d::deep_copy(const array2d &another)
{
    if (data)
    {
        delete[] data;
    }

    row = another.row;
    col = another.col;

    data = new array[row];
    for (int i = 0; i < row; ++i)
    {
        data[i].deep_copy(another.data[i]);
    }//经典二维数组开空间,注意可以调用已经写好的代码,大大提高写代码的效率
}

6-H3

描述

在课后1中,我们已经实现了成员函数deep_copy来进行深拷贝。本题在array的基础上,实现写时复制,来继续完成深拷贝的进阶学习。

观察deep_copy的实现不难发现,在每次进行深拷贝的时候,我们都需要重新分配一块新的内存。在很多时候,array的实例可能只需要调用print函数,不需要调用assign函数,即实例不会对data指向的内存进行修改。对于这些实例,data指向的内存所存放的内容是相同的,因此可以共享,不需要重新分配内存,从而达到减少内存开销的目的。但是,我们并不能提前预知每一个实例在使用过程中是否会对data指向的内存进行修改,不难想到,只需要将深拷贝的过程延迟到实例第一次发生修改的地方即可,这就是写时复制,copy on write。

在本题中,你需要对现有的array类进行改造,并加入ref_countercopy_on_write。为了降低本题难度,这里给出一个写时复制的具体实现:

  • 成员变量ref_counter的作用是标记当前引用data指向的内存的实例数量。
  • 在非复制构造函数中,ref_counter指向的整型变量初始化为1。
  • 当调用deep_copy函数时,并不会执行深拷贝,而是执行浅拷贝,然后对ref_counter进行加1。
  • copy_on_write是真正执行深拷贝的函数。
  • 当需要释放dataref_counter指向的内存时,如果ref_counter指向的值为1,则释放;否则,对ref_counter指向的值减1,不进行释放操作。

复制构造函数已经给出,不需要实现。

参考答案

//main.cpp
#include <iostream>
#include "array.h"

int main()
{
    int n, m;

    std::cin >> n;
    array arr = array(n, -1);

    arr.print();

    for (int i = 0; i < n; ++i)
    {
        std::cin >> m;
        arr.assign(i, m);
    }

    arr.print();
    std::cout << arr.get_ref_counter() << std::endl;
//获取当前引用data指向的内存的实例数量

    array copy_arr = array(arr);

    copy_arr.print();
    std::cout << (copy_arr.get_data() == arr.get_data()) << std::endl
              << copy_arr.get_ref_counter() << std::endl
              << arr.get_ref_counter() << std::endl;

    for (int i = 0; i < n; ++i)
    {
        copy_arr.assign(i, i);
    }

    copy_arr.print();
    arr.print();
    std::cout << (copy_arr.get_data() == arr.get_data()) << std::endl
              << copy_arr.get_ref_counter() << std::endl
              << arr.get_ref_counter() << std::endl;

    copy_arr.deep_copy(array(n / 2, 1));
    copy_arr.print();
    std::cout << copy_arr.get_ref_counter() << std::endl;

    copy_arr.deep_copy(copy_arr);
    std::cout << copy_arr.get_ref_counter() << std::endl;
}
//array.h
#ifndef ARRAY
#define ARRAY

#include <iostream>

class array
{
private:
    int size;         // size
    int *data;        // dynamically allocate/release memory
    int *ref_counter; // reference counter

    void copy_on_write(); // copy on write

public:
    array();
    array(int size, int val);
    array(const array &another)
    {
        data = nullptr;
        ref_counter = nullptr;
        deep_copy(another);
    }
    ~array();
    bool assign(int pos, int val);
    void deep_copy(const array &another);
    void print() const
    {
        for (int i = 0; i < size; ++i)
            std::cout << data[i] << std::endl;
    }
    int get_ref_counter() { return *ref_counter; }
    int *get_data() { return data; }
};

#endif
#include "array.h"
#include <cstdlib>

array::array()
{
    size = 0;
    data = nullptr;
    ref_counter = new int(1);
}

array::array(int size, int val)
{
    this->size = size;
    this->data = new int[size];
    for (int i = 0; i < size; ++i)
    {
        this->data[i] = val;
    }
    ref_counter = new int(1);
}

array::~array()
{
    if (*ref_counter == 1)//如果当前引用位置有内容
    {
        if (this->data)//而且该内容不空
        {
            delete[] this->data;
        }
        delete ref_counter;
    }
    else
    {
        *ref_counter -= 1;//减少一个,还有东西
    }
}

bool array::assign(int pos, int val)
{

    if (pos >= 0 && pos < size)
    {
        copy_on_write();//检查、控制内存的数量
        data[pos] = val;
        return true;
    }
    else
    {
        return false;
    }
}

void array::deep_copy(const array &another)
{
    if (this == &another)
        return;

    if (ref_counter)
    {
        if (*ref_counter == 1)//如果该引用所在空间内只有该引用这一个实例
        {
            if (this->data)
            {
                delete[] this->data;
            }
            delete ref_counter;
        }//就统统删掉,与普通情况相同
        else
        {
            *ref_counter -= 1;
        }//有多个就减去一个
    }

    this->size = another.size;
    this->data = another.data;
    this->ref_counter = another.ref_counter;
    *(this->ref_counter) += 1;//拷贝,之所以可以这样,是因为在控制下,实例数量不是一就是零
}

void array::copy_on_write()
{
    if (*ref_counter == 1)
        return;

    *ref_counter -= 1;//其实他的值要么是1要么是0
//以上是等于1的情况,以下是等于0的情况,即这块空间上啥也没有,想操作得自己建立空间
    ref_counter = new int(1);

    int *new_data = new int[size];
    for (int i = 0; i < this->size; ++i)
    {
        new_data[i] = data[i];
    }
    data = new_data;
}

7-K1

Description

有如下一段程序:

void printtotal(int total) {
    cout<<"Total in Main: "<<total<<endl;
 
}
 int main() {
    int x, y, total;
    cin >> x >> y >> total;
 
    printtotal(total);
    addxy(x, y, total);
 
    printtotal(total);
  
    subxy(x, y, total);
    printtotal(total);
    return 0;
}

请补充函数 addxy() 和 subxy() 的声明和实现.

addxy() 形参中 total 使用按值传递,将totalxy相加得到结果,函数中输出

Total from inside addxy: (具体数字)

subxy() 形参中 total 使用按引用传递,将会修改主函数作用域中的 total 值为|x-y|,函数中输出

Total from inside subxy: (具体数字)

参考答案

#include <iostream>
using namespace std;


void addxy(int a, int b, int c) {
	c = a + b + c;
	cout << "Total from inside addxy: " << c << endl;
}

void subxy(int &a,int &b,int &c) {
	c = a - b;
    if(c < 0) c = -c;
	cout << "Total from inside subxy: " << c << endl;
}
//前者传不回原函数,后者可以

7-K2

Description

设计一个 Double 类来封装double类型数据的操作.
Double 类的定义参考主函数文件

参考答案

//main.cpp
#include "Double.h"
#include<iostream>
using namespace std;

int main()
{
    double num1, num2;
    cin >> num1 >> num2;

    Double a;
    Double b(num1);
    
    cout << a.getDouble() << endl;
    cout << b.getDouble() << endl;


	a.setDouble(num1);
    b.setDouble(num2);
	a.add(b);
    cout << a.getDouble() << endl;

	a.setDouble(num1);
    b.setDouble(num2);    
    a.sub(b);
    cout << a.getDouble() << endl;

	a.setDouble(num1);
    b.setDouble(num2);    
    a.mul(b);
    cout << a.getDouble() << endl;

	a.setDouble(num1);
    b.setDouble(num2);   
    if(a.div(b))
    {
        cout << a.getDouble() << endl;
    }
    else
    {
        cout << "The denominator is zero." << endl;
    }


	return 0;
} 
//double.hpp
#ifndef DOUBLE_H_
#define DOUBLE_H_

#include<iostream>
using namespace std;


class Double
{
private:
     double data;
public:
     Double() ;
     Double(double data);
     double getDouble() const;
     void setDouble(double data);
     void add(const Double &other);
     void sub(const Double &other);
     void mul(const Double &other);
     bool div(const Double &other);
};
//中规中矩的函数头,但是将每一个成员函数的功能显示得清清楚楚
#endif
//double.cpp
#include "Double.h"


Double::Double()
{
	data = 0;
}//无参构造

Double::Double(double data)
{
	Double::data = data;
}//拷贝构造

double Double::getDouble()const						
{
	return data;
}//给外界类私有成员的“只读”权限


void Double::setDouble(double data)
{
	Double::data = data;
}


void Double::add(const Double &other)					
{
	double temp1 = Double::getDouble();
	double temp2 = other.getDouble();
	temp1 = temp1 + temp2;
	Double::setDouble(temp1);
}


void Double::sub(const Double &other)
{
	double temp1 = Double::getDouble();
	double temp2 = other.getDouble();
	temp1 = temp1 - temp2;
	Double::setDouble(temp1);
}

void Double::mul(const Double &other)
{
	double temp1 = Double::getDouble();
	double temp2 = other.getDouble();
	temp1 = temp1 * temp2;
	Double::setDouble(temp1);
}

bool Double::div(const Double &other)
{
	if (other.data  == 0)
		return false;
	else
	{
		double temp1 = Double::getDouble();
		double temp2 = other.getDouble();
		temp1 = temp1 / temp2;
		Double::setDouble(temp1);
	return true;
	}
}//可以用运算符重载简化该运算

7-H1

Description

设计一个k子棋的游戏

  • 棋盘的大小为n∗n, 棋盘左上角坐标为(0,0),右下角坐标为(n−1,n−1)
  • 棋子分为X和O两种
  • 两名棋手各执一种棋子,轮流下棋,规定执O棋子的先手
  • 获胜条件:当一方在横向、纵向或者斜向有k个棋子连成一排(总共8个方向),即为胜利(有平局的可能性)

Ps:
落子位置可能已经存在棋子,所以需要对落子位置进行一定的判断。
当有player胜利时,game结束,之后的落子无效。

*这个题目……其实要考虑6个方向,

横,竖,左上->右下,左上<-右下,左下->右上,左下<-右上.很期待看答案是怎么简化处理的

参考答案

//main.cpp
#include <iostream>
#include "chessboard.h"
using namespace std;

int main() {
    int n, k;
    cin >> n >> k;
    int x, y;

    string chess = "OX";//规定棋子
    int cur = 0;
    ChessBoard chess_board(n, k);
    while (cin >> x >> y, x != -1, y != -1) {
        chess_board.drop(x, y, chess[cur]);
        cur = (cur + 1) % 2;//一来一回算一着

        if (chess_board.check_win())
            return 0;//一步一清算
    }
    chess_board.final_check_win();

    return 0;
}
//chessboard.h
#include <iostream>
using namespace std;

class ChessBoard {
private:
    int n, k;
    char** board;
public:
    ChessBoard(int _n, int _k);
    void drop(int x, int y, char c);
    bool check_win();
    void final_check_win();
    ~ChessBoard();
};
//chessboard.cpp
#include "chessboard.h"
//只有60行?
ChessBoard::ChessBoard(int _n, int _k): n(_n), k(_k) {
    board = new char*[n];
    for (int i = 0; i < n; i ++ ) {
        board[i] = new char[n];
        for (int j = 0; j < n; j++)
            board[i][j] = ' ';
    }
}//开辟空间&初始化

void ChessBoard::drop(int x, int y, char c) {
    if (x < 0 || x >= n || y < 0 || y >= n)
        return;
    if (board[x][y] != ' ')
        return;
    board[x][y] = c;
}//被占用即下了无效棋也算下了

bool ChessBoard::check_win() {
    int dx[4] = {0, 1, 1, 1}, dy[4] = {1, 1, 0, -1};//调控方向
//说是八个方向,其实只要判断四个方向
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ ) {
            if (board[i][j] == ' ')
                continue;
            for (int dir = 0; dir < 4; dir ++ ) {//dir是dx,dy的下标子
                int cur = 1, cx = i, cy = j;//cur是有多少个连续相同的棋
                while (cur < k) {
                    int nx = cx + dx[dir], ny = cy + dy[dir];
//dx,dy一起看
//dir=0,(nx = cx + 0, ny = cy + 1)每次就是向正上走
//dir=1,(nx = cx + 1, ny = cy + 1)每次就是向右上走
//dir=2,(nx = cx + 1, ny = cy + 0)每次就是向正右走
//dir=3,(nx = cx + 1, ny = cy - 1)每次就是向右下走
//可以达到从棋盘的左上到右下的扫描
                    if (nx < 0 || nx >= n || ny < 0 || ny >= n)
                        break;
                    if (board[nx][ny] != board[i][j])
                        break;
                    cur ++ ;
                    cx = nx, cy = ny;
                }

                if (cur >= k) {
                    if (board[i][j] == 'O')
                        cout << "winner: O player\n";
                    else if (board[i][j] == 'X')
                        cout << "winner: X player\n";
                    return true;
                }
            }
        }
    return false;
}


void ChessBoard::final_check_win() {
    if (!check_win())
        cout << "no player win\n";
}

ChessBoard::~ChessBoard() {
    for (int i = 0; i < n; i ++ )
        delete[] board[i];
    delete[] board;
}

7-H2

//感觉这辈子都搞不懂链表了……

Description

双端队列 (deque,全名double−endedqueue)是一种具有队列和栈性质的抽象数据类型。双端队列中的元素可以从两端弹出,插入和删除操作限定在队列的两边进行。

使用双向链表实现双端队列,其中包含的操作说明如下:

  • 插入:
    • 尾部插入:push_back(value),将元素插入deque尾部
    • 头部插入:push_front(value),将元素插入deque头部
  • 删除:删除成功返回true,删除失败返回false
    • 尾部删除:pop_back(),将deque尾部元素删除
    • 头部删除:pop_front(),将deque头部元素删除
  • 查询:
    • 查询尾部:back(),获取deque尾部元素
    • 查询头部:front(),获取deque头部元素
  • 获取大小:len(),获取deque包含的元素个数
  • 判断是否为空:empty(),deque为空返回true,不为空返回false

Ps: 数据保证front()back()操作时deque不为空

参考答案

//main.cpp
#include <iostream>
#include "deque.h"
using namespace std;

int main() {
    int N;
    cin >> N;

    Deque deque;
    while (N -- ) {
        string op;
        cin >> op;//学习如何写简洁又准确的代码

        if (op == "push_back") {
            int x;
            cin >> x;
            deque.push_back(x);
        } else if (op == "push_front") {
            int x;
            cin >> x;
            deque.push_front(x);
        } else if (op == "pop_back") {
            if (deque.pop_back())
                cout << "pop_back success\n";
            else
                cout << "pop_back failed\n";
        } else if (op == "pop_front") {
            if (deque.pop_front())
                cout << "pop_front success\n";
            else
                cout << "pop_front failed\n";
        } else if (op == "back")
            cout << "back value: " << deque.back() << endl;
        else if (op == "front")
            cout << "front value: " << deque.front() << endl;
        else if (op == "len")
            cout << "deque size: " << deque.len() << endl;
        else if (op == "empty") {
            if (deque.empty())
                cout << "deque empty\n";
            else
                cout << "deque not empty\n";
        }
    }
//目录与各种栈的操作
    return 0;
}
//deque.h
#include <iostream>
using namespace std;

class Node {
public:
    int value;
    Node* nx, *pre;

    Node(int _value);
};//节点


class Deque {
private:
    Node* head, *tail;
    int sz;
public:
    Deque();
    void push_front(int value);
    void push_back(int value);
    bool pop_front();
    bool pop_back();
    int front();
    int back();
    int len();
    bool empty();
    ~Deque();
};//链表
//deque.cpp
#include "deque.h"

Node::Node(int _value): value(_value), pre(nullptr), nx(nullptr) {}
//拷贝构造

Deque::Deque() {
    sz = 0;
    head = new Node(-1); // 哨兵节点
    tail = head;//
}

void Deque::push_front(int value) {
    sz ++ ;
    Node* cur = new Node(value);//创建当前指针1
    Node* p = head->nx;//头指针的后一个位置
    if (p != nullptr) {
        cur->nx = p;//p在cur的后面
        p->pre = cur;//cur在p的前面
    } else
        tail = cur;
    head->nx = cur;
    cur->pre = head;//把head留出来
}

void Deque::push_back(int value) {
    sz ++ ;
    Node* cur = new Node(value);
    cur->pre = tail;
    tail->nx = cur;
    tail = cur;
}//替换原来的尾巴,成为新的尾巴

bool Deque::pop_front() {
    if (empty())
        return false;
//删除前一定检查是否为空,避免内存泄漏
    sz -- ;
    Node* p = head->nx, *q = p->nx;
//暂时保留要删除的节点后面的链表
    delete p;
//利用指针指向同一块地址的性质删除目标节点
    if (q != nullptr) {
        head->nx = q;
        q->pre = head;
    } else {
        tail = head;
        head->nx = nullptr;
    }//如果后面啥都没有了,头指针就是尾指针
    return true;
}

bool Deque::pop_back() {
    if (empty())
        return false;

    sz -- ;
    Node* p = tail->pre;
    p->nx = nullptr;
    delete tail;
    tail = p;//删除之前要留一手
    return true;
}

int  Deque::front() {
    return head->nx->value;
}

int Deque::back() {
    return tail->nx->value;
}

int Deque::len() {
    return sz;
}

bool Deque::empty() {
    if (sz == 0)
        return true;
    return false;
}
//了解基本操作,按部就班,我也可以!

Deque::~Deque() {
    while (head != nullptr) {
        Node* p = head->nx;
        delete head;
        head = p;
    }
}

7-H3

Description

设计一个由数组和链表共同组成的存储键值对的数据结构HashDict, 结构图如下:
 

该数据结构的几种操作说明如下:

  • 添加元素:add [key] [value],表示添加一个键值对(key−value),先对key做哈希运算,获取key的hash值,之后用hash值对数组的长度取模获得实际存储位置pos,公式为pos=hash%length,之后将当前键值对插入pos处的链表之中,要求插入之后的链表是按从小到大排序(按key排序)

  • 删除元素:del [key],表示删除键为key的节点,删除成功返回true,删除失败返回false

  • 查询:search [pos],查询位于数组中pos位置的所有节点并输出(需要判断pos是否合法)

  • 扩容:在以下两种情况下需要执行扩容操作:

      1. HashDict中的节点数大于数组长度的2倍
      1. HashDict的数组中某一位置的节点数大于4

    每次扩容操作需要将数组长度变为原来的2倍 +1(如原来的长度为8,扩容后的长度为17),并且将原有的键值对按照添加元素的规则重新添加到新的数组之中。

  • 两个辅助函数:

    • check_cap:判断HashDict容量是否满足要求,满足要求返回true,不满足要求返回false(不满足要求时需要进行扩容)
    • count_pos [key]:计算key的映射位置pos
  1. hash函数:hash=∣3∗key3+5∗key2+7∗key+11∣
  2. 题目确保HashDict中所有的节点的key均不相同
//main.cpp
#include <iostream>
#include "hash_dict.h"
using namespace std;


int main() {
    int L, N;
    cin >> L >> N;

    HashDict hash_dict(L);
    while (N -- ) {
        string op;
        cin >> op;

        if (op == "add") {
            int key;
            string value;
            cin >> key >> value;
            hash_dict.add(key, value);
        } else if (op == "del") {
            int key;
            cin >> key;

            if (hash_dict.del(key))
                cout << "delete success\n";
            else
                cout << "delete failed\n";
        } else if (op == "search") {
            int pos;
            cin >> pos;
            hash_dict.search(pos);
        }
    }//列目录,与前面的题目相似
    return 0;
}
//hash_dict.h
#include <iostream>
using namespace std;

class Node {
public:
    int key;
    string value;
    Node* nx;

    Node(int _key, string _value);
};

class HashDict {
private:
    Node** table;
    int n; // table的长度
    int* cnt; // table中每个pos的节点个数
    int total; // table中总共的节点个数
    void capacity_expand(); // 扩容
    bool check_cap(); // 判断table的容量是否满足要求
    int count_pos(int key); // 计算key对应的pos
public:
    HashDict(int n);
    void add(int key, string value); // 添加节点
    bool del(int key); // 删除节点
    void search(int pos); // 查询
    ~HashDict();
};//有两个类的题型
//hash_dict.cpp
#include "hash_dict.h"

Node::Node(int _key, string _value): key(_key), value(_value), nx(nullptr) {
}//未给指针变量分配空间

HashDict::HashDict(int n) {
    this->n = n;
    total = 0;
    table = new Node*[n];
    cnt = new int[n];
    for (int i = 0; i < n; i ++ ) {
        table[i] = nullptr;
        cnt[i] = 0;
    }
}

bool HashDict::check_cap() {
    if (total > 2 * this->n)
        return false;
    for (int i = 0; i < this->n; i ++ )
        if (cnt[i] > 4)
            return false;
    return true;
}

int HashDict::count_pos(int key) {
    long long hs = 3ll * key * key * key + 5ll * key * key + 7ll * key + 11;
    int pos = hs % this->n;
    return pos;
}

void HashDict::add(int key, string value) {
    Node* node = new Node(key, value);
    int pos = count_pos(key);
//之前没有分配空间,在需要用的时候分配空间
    total ++ ;
    cnt[pos] ++ ;

    Node* p = table[pos], *q = nullptr;
    while (p != nullptr && p->key < key) {
        q = p;
        p = p->nx;
    }
    if (q == nullptr) {
        node->nx = table[pos];
        table[pos] = node;
    } else {
        node->nx = q->nx;
        q->nx = node;
    }

    if (!check_cap())
        capacity_expand();
}

bool HashDict::del(int key) {
    int pos = count_pos(key);

    Node* p = table[pos], *q = nullptr;
    while (p != nullptr && p->key != key) {
        q = p;
        p = p->nx;
    }
    if (p == nullptr)
        return false;
    if (q == nullptr)
        table[pos] = p->nx;
    else
        q->nx = p->nx;
    delete p;
    return true;
}

void HashDict::capacity_expand() {
    int m = n;
    n = 2 * n + 1;
    Node** tmp_table = table;
    delete[] cnt;

    table = new Node*[n];
    cnt = new int[n];
    total = 0;
    for (int i = 0; i < n; i ++ ) {
        table[i] = nullptr;
        cnt[i] = 0;
    }

    for (int i = 0; i < m; i ++ ) {
        Node* p = tmp_table[i];
        tmp_table[i] = nullptr;

        while (p != nullptr) {
            Node* q = p->nx;
            add(p->key, p->value);
            delete p;
            p = q;
        }
    }
    delete[] tmp_table;
}

void HashDict::search(int pos) {
    cout << "pos " << pos << ": ";
    if (pos >= n) {
        cout << endl;
        return;
    }

    int num = 0;
    Node* p = table[pos];
    while (p != nullptr) {
        if (num > 0)
            cout << "->";
        cout << p->key << ":" << p->value;
        p = p->nx;
        num ++ ;
    }
    cout << endl;
}

HashDict::~HashDict() {
    delete[] cnt;

    for (int i = 0; i < n; i ++ ) {
        Node* p = table[i];
        table[i] = nullptr;

        while (p != nullptr) {
            Node* q = p->nx;
            delete p;
            p = q;
        }
    }
    delete[] table;
}//拷贝前保证即将到达的空间为空

8-K1

重载操作符(),使得能够像调用函数一样使用对象(函数对象)

class Exchange {

         public: void operator()(int&, int&);

};

运算符重载的方法:类成员函数,友元函数

左操作数必须是*this

右操作数任意类型,但是一般都加了const

返回值一般为临时对象或者自身的引用,看需要什么

移位运算符需要类外定义(第一操作数不是*this,返回自身的引用)

#include <iostream>
#include <cstdlib>
using namespace std;

class Exchange
{
    public:
	void operator()(int&, int&);
};

void Exchange::operator()(int&a, int&b){
    int t=a;a=b,b=t;
}
//学习运算符重载的格式
int main()
{
   Exchange swap;
   int i,j;
   cin >> i >> j;
   swap(i, j);
   cout << i << " " << j << endl;
   return 0;
}


8-K2

Description

实现复数的操作符重载,你需要做的是,将complex.h文件的函数声明,全部在complex.cpp中实现。

参考答案

//main.cpp
#include <vector>
#include <numeric>
#include <iostream>
#include<cstdlib>
#include "complex.h"
using namespace std;

int main()
{
    int val_1,val_2,val_3;
    cin >> val_1 >> val_2;
    COMPLEX c1(val_1, val_2); // 定义一个值为val_1 + val_2*i的复数c1
    cin >> val_3;
    COMPLEX c2(val_3);    // 定义一个值为val_3 + 0*i的复数c2
    COMPLEX c3(c1);   // 用拷贝构造函数创建一个值同c1的新复数

    c3.print();        // 打印c3的值
    c1 = c1 + c2 + c3; // 将c1加上c2再加上c3赋值给c1
    c2 = -c3;          // c2等于c3求负
    c3 = c2 - c1;      // c3等于c2减去c1
    c3.print();        // 再打印运算后c3的值

    COMPLEX temp= c3++;
    temp.print();
    c3.print();

    temp=c3--;
    temp.print();
    c3.print();

    temp=--c3;
    temp.print();
    c3.print();

    temp=++c3;
    temp.print();
    c3.print();   

    return 0;
}

//complex.cpp
#ifndef COMPLEX_H
#define COMPLEX_H

class COMPLEX
{
public:
	COMPLEX(double r = 0, double i = 0); // 构造函数
	COMPLEX(const COMPLEX &other);		 // 拷贝构造函数
	void print();						 // 打印复数
	
	COMPLEX operator+(const COMPLEX &other); // 重载加法运算符(二元)
	COMPLEX operator-(const COMPLEX &other); // 重载减法运算符(二元)
	COMPLEX operator-(); // 重载求负运算符(一元)
	COMPLEX operator=(const COMPLEX &other); // 重载赋值运算符(二元)
  
	COMPLEX &operator++();   //重载前置++
	COMPLEX operator++(int); //重载后置++
	COMPLEX &operator--();   //重载前置--
	COMPLEX operator--(int); //重载后置--
protected:
	double real, image; // 复数的实部与虚部
};
#endif

//这些在考前要背下来
//complex.cpp
#include "complex.h"
#include <iostream>
COMPLEX::COMPLEX(double r, double i)
{
	real = r;
	image = i;
}

COMPLEX::COMPLEX(const COMPLEX& other)
{
	real = other.real;
	image = other.image;
}




COMPLEX COMPLEX::operator+(const COMPLEX &other)
{
    COMPLEX temp;
    temp.real = real + other.real;
    temp.image = image + other.image;
    return temp;
}

COMPLEX COMPLEX::operator-(const COMPLEX &other)
{
    COMPLEX temp;
    temp.real = real - other.real;
    temp.image = image - other.image;
    return temp;
}
COMPLEX COMPLEX::operator-()
{
    COMPLEX temp;

    temp.real = -real;
    temp.image = -image;
    return temp;
}

COMPLEX COMPLEX::operator=(const COMPLEX &other)
{
    real = other.real;
    image = other.image;
    return *this;
}

void COMPLEX::print()
{
    std::cout << real;
    if (image > 0)
        std::cout << "+" << image << "i";
    else if (image < 0)
        std::cout << image << "i";
    std::cout << std::endl;
}

COMPLEX& COMPLEX::operator++()      //COMPLEX.CPP
{
	real += 1;
	image += 1;
	return *this;
}

COMPLEX COMPLEX::operator++(int)
{
	COMPLEX before = *this;
	real += 1;
	image += 1;

	return before;
}
//终于知道怎么即返回加之前的值又更改了当前值
//秘诀是:1.先将原来的值拷贝到新的空间,再修改,最后返回拷贝后值,指针值自己已经修改
COMPLEX& COMPLEX::operator--()      //COMPLEX.CPP
{
	real -= 1;
	image -= 1;
	return *this;
}

COMPLEX COMPLEX::operator -- (int)
{
	COMPLEX before = *this;
	real -= 1;
	image -= 1;

	return before;
}


8-H1

综述

何为队列

队列(Queue)是一种先进先出(First in first out, FIFO)的线性数据结构。从本质上来讲,队列是一种特殊的数组——它只能在数组的其中一端(队头)删除元素,并且只能在另一端(队尾)插入元素。
队列的经典操作包括入队出队获取队列大小获取队头元素获取队尾元素等。在本题中,我们会实现前3个经典的操作(即入队、出队、获取队列大小)。并且和传统的队列结构不同的是,本题支持使用下标索引"[ ]"来实现对队列中元素的随机访问。

队列的实现

队列的实现方式有很多种,循环数组是其中一种非常简单、经典的实现方式,它采用数组来存储队列中的数据,而使用两个索引值front和back来模拟队头和队尾。所谓循环数组,就是指当填充数据的位置超过数组的最大索引值时,改为在数组的头部进行数据填充,在非循环的线性物理结构数组上,维护其循环的假象
**

本题会基于循环数组来实现这个队列。可能有同学在此前了解过队列数据结构的一些基本操作(如入队push()、出队pop()等)。在本题的实现中,请用运算符"+="来实现数据的入队操作,请用运算符"--"来实现出队操作。
提示:本题中队列的最大长度不超过100,这也就意味着同时最多有100个元素能够被存储在队列内。

实现细节

看完上面的综述,依然不知道队列如何实现?没有关系!请详细阅读下面的细节实现描述,它会帮助大家从零开始实现这个OperatorLinkedQueue。

私有成员

int arr[]:用于存储队列中的数据。本题中队列最大长度为100,因此可以自然而然想到要为arr分配一个大小为至少100个int的动态空间。
int front:队头所在索引。队列中所有的删除(出队)操作都是在数组中的front位置进行的。**int back:队尾所在索引。队列中所有的插入(入队)操作都是在数组中的back位置进行的。
int size:队列当前已容纳元素数量。由于在循环数组实现的队列中,无论在队空状态下还是队满状态下,都满足front == back。所以我们可以额外使用一个size变量,来更方便地获取目前队列中容纳的数据量,并判断队列是否为空/为满。

公有成员

OperatorLinkedQueue():默认构造函数,为arr新分配能容纳100个int的空间。
OperatorLinkedQueue(const OperatorLinkedQueue&):拷贝构造函数,注意arr指针的复制操作(懂得都懂)。
~OperatorLinkedQueue():析构函数。很简单,只是要释放arr动态申请的内存空间。**
int GetSize() const:返回当前队列的容纳数据量(size)。
int & operator[] (const int &index):按索引index访问队列中的元素,返回索引表示可对队列中元素进行修改。注意,这里的index是逻辑上元素相对于队头的位置(比如index=2,就是队伍中排在第3个的元素,而不是arr[2])。
int operator[] (const int &index) const:和上面一样,不过这里返回的是队列中的元素的拷贝。其实方法的实现是完全一样的,只是这里去掉了'&'引用符号,并添加了const关键字而已。

OperatorLinkedQueue operator= (const OperatorLinkedQueue&):重载赋值运算符,可以直接调用拷贝构造函数(只要你拷贝构造函数传参时加了const关键字)。
OperatorLinkedQueue operator+= (const int &):在队伍的最后端添加一个元素,模拟入队操作。如果此时队伍元素已满,则不进行任何操作。
OperatorLinkedQueue operator-- ():自减运算符,在队伍的最前端删除一个元素,模拟出队操作。如果此时队伍元素为空,则不进行任何操作。
OperatorLinkedQueue operator-- (int):自减运算符,和上面的类似,唯一区别在于上面的是--OperatorLinkedQueue,而这里是OperatorLinkedQueue--。大家可以回忆一下--运算符放在前面和后面的区别。
friend ostream& operator << (ostream &, const OperatorLinkedList &):重载输出运算符。我们希望在输出的时候,用"->"来依次连接每个元素的输出(比如"1->3->5->7"),最后一个元素后面不需要添加"->"。

 //虽然真的很复杂,但是很有用

参考答案

//main.cpp
#include "OperatorLinkedQueue.h"


int main() {
	
	int n;
	cin >> n;
	
	OperatorLinkedQueue queue1;
	for(int i=0; i<n; ++i) {
		int var;
		cin >> var;
		queue1 += var;
	}
	
	cout << queue1 << endl;
	
	OperatorLinkedQueue queue4(queue1);
	
	OperatorLinkedQueue queue2 = queue1--;
	cout << queue2 << endl;
	
	OperatorLinkedQueue queue3 = --queue1;
	cout << queue3 << endl;
	
	cout << queue4 << endl;

	return 0;
}

//OperatorLinkedQueue.h
#ifndef OPERATORLINKEDQUEUE_H
#define OPERATORLINKEDQUEUE_H
#include <iostream>
using namespace std;


class OperatorLinkedQueue {
	
private:
	int *arr;
	int size;
	int front;
	int back;
	
public:
	
	OperatorLinkedQueue();
	OperatorLinkedQueue(const OperatorLinkedQueue &other);
	~OperatorLinkedQueue();
	
	int GetSize() const;
	
	int & operator[] (const int &index);
	int operator[] (const int &index) const;
	OperatorLinkedQueue operator= (const OperatorLinkedQueue &right);
	OperatorLinkedQueue operator+= (const int &ele);
	OperatorLinkedQueue operator-- ();
	OperatorLinkedQueue operator-- (int);
	
	friend ostream & operator << (ostream &os, const OperatorLinkedQueue &object);
};

#endif

//OperatorLinkedQueue.cpp

#include "OperatorLinkedQueue.h"
#include <cstring>


OperatorLinkedQueue::OperatorLinkedQueue() {
	arr = new int[100];
	size = 0;
	front = 0;
	back = 0;
}


OperatorLinkedQueue::OperatorLinkedQueue(const OperatorLinkedQueue &other) {
	this->arr = new int[100];
	this->size = other.size;
	this->front = 0;
	this->back = this->size;
	for(int i=0; i<this->size; ++i) {
		this->arr[i] = other[i];
	}
}


OperatorLinkedQueue::~OperatorLinkedQueue() {
	delete[] arr;
}


int OperatorLinkedQueue::GetSize() const {
	return this->size;
}


int & OperatorLinkedQueue::operator[] (const int &index) {
	return this->arr[(this->front+index) % 100];
}


int OperatorLinkedQueue::operator[] (const int &index) const {
	return this->arr[(this->front+index) % 100];
}
//意思就是这个循环数组有100个位置

OperatorLinkedQueue OperatorLinkedQueue::operator= (const OperatorLinkedQueue &right) {
	if (this != &right) {
		delete []arr;
		this->arr = new int[100];
		this->size = right.size;
		this->front = 0;
		this->back = this->size;
		for(int i=0; i<this->size; ++i) {
			this->arr[i] = right[i];
		}
	}
	return *this;
}


OperatorLinkedQueue OperatorLinkedQueue::operator+= (const int &ele) {
	if(this->size < 100) {
		this->arr[this->back++] = ele;
		this->back %= 100;
		++ this->size;
	}
	return *this;
}


OperatorLinkedQueue OperatorLinkedQueue::operator-- () {
	if(this->size > 0) {
		++ this->front;
		this->front %= 100;
		-- this->size;
	}
	return *this;
}


OperatorLinkedQueue OperatorLinkedQueue::operator-- (int) {
	OperatorLinkedQueue result(*this);
	-- *this;
	return result;
}//实现后置++


ostream & operator << (ostream &os, const OperatorLinkedQueue &object) {
	if(object.GetSize() > 0) {
		for(int i=0; i<object.GetSize()-1; ++i) {
			os << object[i] << "->";
		}
		os << object[object.GetSize()-1];
	}
	return os;
}

8-H2

Description

In this assignment you are required to complete a class for fractions.

It stores the numerator and the denominator, with the lowest terms.

It is able to communicate with iostreams by the operator << and >>.

For more details, read the header file.

//说人话就是利用运算符重载实现分数的运算

//当初改了好久,期待答案的简洁又厉害的方法

参考答案

//main.cpp
#include "fraction.h"

void print(const bool & f) {
    if (f)
        std::cout << "True" << std::endl;
    else
        std::cout << "False" << std::endl;
}

int main() {
    fraction f1, f2;
    std::cin >> f1 >> f2;
    std::cout << f1 + f2 << ' ' << f1 - f2 << ' '
              << f1 * f2 << ' ' << f1 / f2 << std::endl;
    f1 += f2;
    std::cout << f1 << std::endl;
    f1 *= f2;
    std::cout << f1 << std::endl;
    f1 -= f2;
    std::cout << f1 << std::endl;
    f1 /= f2;
    std::cout << f1 << std::endl;
    print(f1 == f2);
    print(f1 != f2);
    print(f1 < f2);
    print(f1 > f2);
    print(f1 <= f2);
    print(f1 >= f2);
    return 0;
}


//fraction.h
#ifndef FRACTION_H
#define FRACTION_H

#include <iostream>

class fraction {
    private:
        int _numerator, _denominator;
        int gcd(const int &, const int &) const;
            // If you don't need this method, just ignore it.
        void simp();
            // To get the lowest terms.
    public:
        fraction(const int & = 0, const int & = 1);
//好好学习这种类型的构造函数怎么初始化
            // The numerator and the denominator
            // fraction(5) = 5/1 = 5 :)
        fraction(const fraction &);
            // copy constructor

        void operator=(const fraction &);

        // You must know the meaning of +-*/, don't you ?
        fraction operator+(const fraction &) const;
        fraction operator-(const fraction &) const;
        fraction operator*(const fraction &) const;
        fraction operator/(const fraction &) const;

        void operator+=(const fraction &);
        void operator-=(const fraction &);
        void operator*=(const fraction &);
        void operator/=(const fraction &);

        // Comparison operators
        bool operator==(const fraction &) const;
        bool operator!=(const fraction &) const;
        bool operator<(const fraction &) const;
        bool operator>(const fraction &) const;
        bool operator<=(const fraction &) const;
        bool operator>=(const fraction &) const;

        friend std::istream & operator>>(std::istream &, fraction &);
            // Input Format: two integers with a space in it
            // "a b" is correct. Not "a/b"
        friend std::ostream & operator<<(std::ostream &, const fraction &);
            // Normally you should output "a/b" without any space and LF
            // Sometims you may output a single integer (Why? Guess XD)
            // If it is not a number (den = 0), output "NaN"
};

#endif


//fraction.cpp
#include <iostream>
#include "fraction.h"
using namespace std;
typedef long long LL;

#ifndef __FRACTION__
#define __FRACTION__

	#define P _numerator
	#define Q _denominator
	#define _NaN fraction(0,0)

#endif

LL gcd(LL a,LL b)	{	return !b?a:gcd(b,a%b);	}
//请记住,如何快速求最大公约数
/*
int gcd(int x, int y)
{
    return x % y ? gcd(y, x % y) : y;
}
*/

int fraction::gcd(const int &a, const int &b) const	{
	return !b?a:gcd(b,a%b);
}
void fraction::simp()	{
	if (!P&&!Q)	return ;
	int g=gcd(P,Q);
	P/=g,Q/=g;
	if (Q<0)
		P=-P,Q=-Q;
}//保证分母是正数

fraction f(LL p,LL q)	{
	if (!p&&!q)	return _NaN;
	LL g=gcd(p,q);
	p/=g,q/=g;
	if (q<0)
		p=-p,q=-q;
	return fraction(p,q);
}//返回化简后的分数

fraction::fraction(const int & p, const int & q)	{	
	P=p,Q=q;
	simp();
}//可以使分数一出场就是化简了的
fraction::fraction(const fraction &F)	{
	P=F.P,Q=F.Q;	
}

void fraction::operator=(const fraction &F)	{
	P=F.P,Q=F.Q;
}//没有指针成员就可以直接复制

fraction fraction::operator+(const fraction &F) const	{
	if (!Q||!F.Q)	return _NaN;
	return  f(1LL*P*F.Q+1LL*Q*F.P,1LL*Q*F.Q);
//LL其实代表long long, * 1LL是为了在计算时,把int类型的变量转化为long long,然后再赋值给long long类型的变量
} //打字真的少了好多,遇到超级长的变量名,学习如何化简,提高变成效率
fraction fraction::operator-(const fraction &F) const	{
	if (!Q||!F.Q)	return _NaN;
	return  f(1LL*P*F.Q-1LL*Q*F.P,1LL*Q*F.Q);
}
fraction fraction::operator*(const fraction &F) const	{
	if (!Q||!F.Q)	return _NaN;
	return  f(1LL*P*F.P,1LL*Q*F.Q);
}
fraction fraction::operator/(const fraction &F) const	{
	if (!Q||!F.Q)	return _NaN;
	return  f(1LL*P*F.Q,1LL*Q*F.P);
}

void fraction::operator+=(const fraction &F)	{	*this=*this+F;	}
void fraction::operator-=(const fraction &F)	{	*this=*this-F;	}
void fraction::operator*=(const fraction &F)	{	*this=*this*F;	}
void fraction::operator/=(const fraction &F)	{	*this=*this/F;	}
//充分利用已经写完的代码
bool fraction::operator==(const fraction &F) const	{
	return (!Q&&!F.Q)||(Q&&F.Q&&1LL*P*F.Q==1LL*Q*F.P); 
}//充分利用分母不为零的条件,避免浮点数比较带来的一系列麻烦
bool fraction::operator!=(const fraction &F) const	{	return !(*this==F);	}
bool fraction::operator<(const fraction &F) const	{
	return Q&&F.Q&&1LL*P*F.Q<1LL*F.P*Q;
}
bool fraction::operator>(const fraction &F) const	{	
	return Q&&F.Q&&1LL*P*F.Q>1LL*F.P*Q;
}
bool fraction::operator<=(const fraction &F) const	{
	return Q&&F.Q&&1LL*P*F.Q<=1LL*F.P*Q;
}
bool fraction::operator>=(const fraction &F) const	{	
	return Q&&F.Q&&1LL*P*F.Q>=1LL*F.P*Q;
}

std::istream & operator>>(std::istream &is, fraction &F)	{
	is>>F.P>>F.Q;
	F.simp();
	return is;
} 
std::ostream & operator<<(std::ostream &os, const fraction &F)	{
	if (!F.Q)
		os<<"NaN";
	else	{
		os<<F.p;
		if (F.Q!=1)
			os<<"/"<<F.Q; 
	}
	return os;
}
//好好学怎么重载<<运算符
//姜还是老的辣,好好学,快快背

not a number (den = 0), output "NaN"

8-H3

Description

注意:以下世界观内容仿制《辐射4》背景故事,纯属虚构,切勿当真!

After the nuclear firestorm in year 2077, human civilization is on the verge of destruction, but new civilizations still exist on the old and glorious wreckage. Survivors started building underground Safehouse to avoid ground fallout and explore new living place. A Safehouse can not only provide rooms for surviving, but also store many necessary types of living Resource.

在2077年的末世浩劫“核火风暴”之后,人类文明毁灭了,但没有完全毁灭,新的文明依然存在于旧日辉煌的残骸之上。幸存者为了躲避地面的辐射,找到新的生存空间,开始修建起地下避难所(Safehouse)。地下避难所不仅仅能提供人们居住的环境,而且还能存储各种各样的资源(Resource)

Guiding

Assume that any kinds of Resource has its name and count in management.

假设在资源管理的概念中,每一种资源(Resource)都有对应的名称(name) 和数量(count)

struct Resource {
  string name;
  int count;

  Resource(): name(""), count(0) {}
  Resource(const string& str, const int& c): name(str), count(c) {}
};

And a Safehouse can be regarded as a place for storing these kinds of Resource.

而一个避难所(Safehouse)可以被视为存储这些资源的地方

class Safehouse {
private:
  Resource* _owns;  // A place for storing many types of resources.
  int _count_own_types;  // How many types of resources has this Savehouse stored.

public:
   // Some functions.
};

There are up to 16 kinds of Resource in the world, and a Safehouse may contain several kinds of them, each of which is of corresponding amount (or count). To manage the Resource in Safehouse, the regulator decided to use overloaded operator to express Resource changes, for instance, so here is the task for you: implement the operator overloading of class Safehouse, since you are the cleverest among all residents.

这个世界上最多一共有16种资源,每个避难所可能存储其中的几种,每一种资源都有其对应的数量。为了方便管理避难所的资源存储,避难所监管者决定采用重载的运算符(overloaded operator)形式来表示避难所的资源存储变化概况。现在请你——避难所里最聪明的居民,来帮助他实现避难所的资源数量运算

Details

The Implementation of Resource struct is not needed, because it has been placed together with the announcement of class Safehouse in file "Safehouse.h". All you need to do is to realize some operator overloading functions of class Safehouse.

你不需要实现用于表示资源的结构体,因为它的定义和避难所类的声明都放在"Safehouse.h"头文件中。你只需要为避难所类实现如下这些运算符重载即可。

// "Copy" all Resource from another Safehouse to current Safehouse.
Safehouse& operator = (const Safehouse& another);
​
// Add a kind of Resource to target Safehouse.
// If the Resource type ALREADY exists in target Safehouse, just add on its existing count.
// If the Resource type NOT exists in target Safehouse, add the new type totarget Safehouse with corresponding count.
Safehouse operator + (const Resource& new_resource); 
​
// Add all Resource existing in another Safehouse to target Safehouse.
// You can call the overloaded version of '+ Resource' above for convenience.
Safehouse operator + (const Safehouse& another);
​
// Similar to the overloading of '+ Resource'.
Safehouse& operator += (const Resource& new_resource);
​
// Similar to the overloading of '+ Safehouse'.
Safehouse& operator += (const Safehouse& another);
​
// Get the Resource object at designated index. You can assume that all input indexs are VALID!
const Resource& operator [] (const int& index) const;
​
// Compare current Safehouse with another Safehouse.
// "Safehouse1 >= Safehouse2" is satisfied, if and only if each of all Resource types existing in Safehouse2 also exists in Safehouse1.
// Besides, as for a type of Resource, the corresponding amount in Safehouse1 must be NOT LESS THAN the corresponding amount in Safehouse2.
// Otherwise, "Safehouse1 >= Safehouse2" is NOT satisfied.
bool operator >= (const Safehouse& another);
​
// Print all existing Resource in target Safehouse.
// The form of output is shown in Sample Output.
friend ostream& operator << (ostream& os, const Safehouse& house);

Hint

Some useful functions of class Safehouse have been implemented. You can use them for simplifying your own codes, or just ignore them.

在Safehouse类中实现了一些可以直接调用的函数。你可以调用它们以简化自己代码,也可以选择无视它们。

//题目长度是纸老虎,按题目要求一个一个实现就可以啦

参考答案

//main.cpp
#include "Safehouse.h"

/*

Available resource types:
- Oil
- Coal
- Gunpowder
- Leather
- Plastic
- Rubber
- Lewel
- Steel
- Iron
- Copper
- Zinc 
- Wood
- Glass
- Fibre
- Grass
- Electronic

*/


istream& operator >> (istream& is, Resource& r) {
	is >> r.name >> r.count;
	return is;
}


int main() {
	
	Safehouse house1;
	for(int i=0; i<MAX_RESOURCE_TYPES; ++i) {
		Resource new_resource;
		cin >> new_resource;
		house1 += new_resource;
		
		cout << "After adding " << new_resource.count << " " << new_resource.name << " to house1 >>>" << endl;
		cout << house1 << endl;
	}
	
	Safehouse house2; 
	for(int i=0; i<house1.GetCountOwnTypes()/2; ++i) {
		house2 = house2 + house1[i*2];
		
		cout << "After adding " << house1.GetOwnsCount(i*2) << " " << house1.GetOwnsName(i*2) << " to house2 >>>" << endl;
		cout << house2 << endl;
	}
	cout << "house2 >= house1? : " << boolalpha << (house2 >= house1) << endl;
	
	
	Safehouse house3;
	house3 = house1 + house3;
	house3 += house2; 
	cout << "After adding house1 and house2 to house3 >>>" << endl;
	cout << house3 << endl;
	cout << "house3 >= house1? : " << boolalpha << (house3 >= house1) << endl;
	
	Safehouse house4 = house3;
	house4 = house4;
	cout << "After initializing house4 with house3 >>> " << endl;
	cout << house4 << endl;
	
	return 0;
}

//Safehouse.h
#ifndef __SAFEHOUSE__
#define __SAFEHOUSE__

#define MAX_RESOURCE_TYPES 16

#include <iostream>
#include <string>

using namespace std;


struct Resource {
	string name;
	int count;
	
	Resource(): name(""), count(0) {}
	Resource(const string& str, const int& c): name(str), count(c) {}
};


class Safehouse {
	
private:
	Resource* _owns;
	int _count_own_types;
	
public:
	
	Safehouse(): _owns(new Resource[MAX_RESOURCE_TYPES]), _count_own_types(0) {}
	~Safehouse() {if(_owns) delete[] _owns;}
	
	// Below are some supporting functions.
	
	// Copy constuctor for class Safehouse. 
	Safehouse(const Safehouse& another): _owns(new Resource[MAX_RESOURCE_TYPES]), _count_own_types(another._count_own_types) {
		for(int i=0; i<_count_own_types; ++i) {
			_owns[i].name = another._owns[i].name;
			_owns[i].count = another._owns[i].count;
		}
	}
	
	// Return how many different types of Resource does this Safehouse contain.
	int GetCountOwnTypes() const {
		return _count_own_types;
	}
	
	// Get the Resource name on designated index.
	string GetOwnsName(const int& index) const {
		if(index >= _count_own_types || index < 0) {
			return "None";
		}
		else {
			return _owns[index].name;
		}
	}
	
	// Get the Resource count on designated index.
	int GetOwnsCount(const int& index) const {
		if(index >= _count_own_types || index < 0) {
			return 0;
		}
		else {
			return _owns[index].count;
		}
	}
	
	// Find the first index of designated Resource type, return -1 if the designated Resource does not exist in Safehouse yet. 
	int IndexOfResource(const string& resource_name) const {
		for(int i=0; i<_count_own_types; ++i) {
			if(_owns[i].name == resource_name) {
				return i;
			}
		}
		return -1;
	}
	
	// Find the count of designated Resource type, return 0 if the designated Resource does not exist in Safehouse yet.
	int CountOfResource(const string& resource_name) const {
		for(int i=0; i<_count_own_types; ++i) {
			if(_owns[i].name == resource_name) {
				return _owns[i].count;
			}
		}
		return 0;
	}
	
	// Your Implementation.
	Safehouse& operator = (const Safehouse& another);
	Safehouse operator + (const Resource& new_resource);
	Safehouse operator + (const Safehouse& another);
	Safehouse& operator += (const Resource& new_resource);
	Safehouse& operator += (const Safehouse& another);
	const Resource& operator [] (const int& index) const;
	bool operator >= (const Safehouse& target);
	friend ostream& operator << (ostream& os, const Safehouse& house);
	
};

#endif 

//Safehouse.cpp
#include "Safehouse.h"


Safehouse& Safehouse::operator = (const Safehouse& another) {
	
	if(this == &another) {
		return *this;
	}
	
	delete[] _owns;
	_owns = new Resource[MAX_RESOURCE_TYPES];
	_count_own_types = another._count_own_types;
	
	for(int i=0; i<_count_own_types; ++i) {
		_owns[i].name = another._owns[i].name;
		_owns[i].count = another._owns[i].count;
	}
	
	return *this;
}


Safehouse Safehouse::operator + (const Resource& new_resource) {
	
	Safehouse sf(*this);
	
	int resource_index = sf.IndexOfResource(new_resource.name);
	if(resource_index == -1) {
		sf._owns[sf._count_own_types].name = new_resource.name;
		sf._owns[sf._count_own_types].count = new_resource.count;
		++ sf._count_own_types;
	}
	else {
		sf._owns[resource_index].count += new_resource.count;
	}
	
	return sf;
}


Safehouse Safehouse::operator + (const Safehouse& another) {
	
	Safehouse sf(*this);
	for(int i=0; i<another._count_own_types; ++i) {
		sf = sf + another._owns[i];
	}
	
	return sf;
}


Safehouse& Safehouse::operator += (const Resource& new_resource) {
	
	int resource_index = IndexOfResource(new_resource.name);
	if(resource_index == -1) {
		_owns[_count_own_types].name = new_resource.name;
		_owns[_count_own_types].count = new_resource.count;
		++ _count_own_types;
	}
	else {
		_owns[resource_index].count += new_resource.count;
	}
	
	return (*this);
}


Safehouse& Safehouse::operator += (const Safehouse& another) {
	for(int i=0; i<another._count_own_types; ++i) {
		(*this) += another._owns[i];
	}
	
	return (*this);
}
//有指针的情况,要注意是否要更改原因用的值?
//要的话返回该指针的引用,不要的话就返回一个可能存在的拷贝值

const Resource& Safehouse::operator [] (const int& index) const {
	if(_count_own_types == 0 || index < 0 || index >= _count_own_types) {
		return move(Resource());
	}
	else {
		return _owns[index];
	}
}


bool Safehouse::operator >= (const Safehouse& target) {
	for(int i=0; i<target._count_own_types; ++i) {
		int my_count = CountOfResource(target._owns[i].name);
		if(my_count < target._owns[i].count) {
			return false;
		}
	}
	
	return true;
}


ostream& operator << (ostream& os, const Safehouse& house) {
	for(int i=0; i<house.GetCountOwnTypes(); ++i) {
		os << house.GetOwnsName(i) << ": " << house.GetOwnsCount(i) << endl;
	}
	return os;
}

9-K1

很多STL算法都使用​**函数对象——也叫函数符(functor)**​。函数符是可以以函数方式与()结合使用的任意对象。其中包括了函数名、指向函数的指针和重载了()运算符的类对象(即定义了函数operator()()的类)。函数对象是一个对象,但是使用的形式看起来像函数调用,实际上也执行了函数调用,因而得名。

参考答案

#include <iostream>
using namespace std;
class OddEven
{
public:
    bool operator()(int a1,int a2,int a3);
};

bool OddEven::operator()(int a1,int a2,int a3){
    return (a1 * a2 * a3) % 2;
}
//学习怎么重载括号
int main()
{
    int a1,a2,a3;
    cin>>a1>>a2>>a3;
    OddEven f1;  //能够求三个整数相乘奇偶性的函数对象
    if(f1(a1,a2,a3))
        cout <<"Odd";  
    else 
        cout <<"Even";
    return 0;
}

9-K2

Description

假设有一个名为 Rectangle 的类,用于表示矩形。该类有两个私有成员变量 width 和 height,用于表示矩形的宽和高。类中有一个公有成员函数 getArea(),用于计算矩形的面积。现在需要在 Rectangle 类中添加一个友元函数 isSquare(Rectangle rect),用于判断给定的矩形是否是正方形。

请你完成以下任务:

  1. 实现 Rectangle 类和 getArea() 函数。
  2. 实现 isSquare(Rectangle rect) 友元函数,用于判断给定的矩形是否是正方形。如果是正方形,返回 true;否则,返回 false。
  3. 实现 操作运算符< 用于比较两个矩形面积大小。

参考答案

//main.cpp
#include <iostream>
#include <Rectangle.hpp>
using namespace std;

int main()
{   
    int w1,h1,w2,h2;
    cin>>w1>>h1>>w2>>h2;
    Rectangle rect1(w1, h1);
    Rectangle rect2(w2, h2);

    if(isSquare(rect1))
        std::cout << "rect1 is a square" <<std::endl;
    else 
        std::cout << "rect1 is not a square" <<std::endl;
    if(isSquare(rect2))
        std::cout << "rect2 is a square" << std::endl;
    else 
        std::cout << "rect2 is not a square" << std::endl;

    if(rect1<rect2)
        cout<<"rect2's area bigger than rect1"<<endl;
    else
        cout<<"rect1's area bigger than rect2"<<endl;
        
  return 0;
}
//Rectangle.hpp
#include <iostream>

class Rectangle {
public:
  Rectangle(int w, int h) : width_(w), height_(h) {}
  
  int getArea() const {
    return width_ * height_;
  }

  friend bool isSquare(Rectangle rect);
  friend bool operator <(Rectangle rect1,Rectangle rect2);
private:
  int width_;
  int height_;
};

bool isSquare(Rectangle rect) {
  return rect.width_ == rect.height_;
}

bool operator < (Rectangle rec1, Rectangle rec2) {
    return rec1.getArea()<rec2.getArea();
}
//学习使用友元函数

9-H1

老师说不会写的情况下抄也是可以的,只是抄几遍要会自己写了。

Description

The rabbits have powerful reproduction ability. One pair of adult rabbits can give birth to one pair of kid rabbits every month. And after m months, the kid rabbits can become adult rabbits.

As we all know, when m=2, the sequence of the number of pairs of rabbits in each month is called Fibonacci sequence. But when m​=2, the problem seems not so simple. You job is to calculate after d months, how many pairs of the rabbits are there if there is exactly one pair of adult rabbits initially. You may assume that none of the rabbits dies in this period.

Input

The input may have multiple test cases. In each test case, there is one line having two integers m(1≤m≤10),d(1≤d≤100), m is the number of months after which kid rabbits can become adult rabbits, and d is the number of months after which you should calculate the number of pairs of rabbits. The input will be terminated by m=d=0.

Output

You must print the number of pairs of rabbits after d months, one integer per line.

Sample Input

2 3
3 5
1 100
0 0
//目标是实现加号的重载
#include<iostream>
#include<vector>//啥
#include<iomanip>//啥
#define maxn 105//既不要等号也不要分号
using namespace std;//这是需要分号的
BigInt dp[maxn];//出现这种类型多半是静态类型
int main()
{
    int m,n;
    while(cin>>m>>n,m&&n)//大进小出{
        for(int i=1;i<=m;++i)    dp[i] =1;
        for(int i=m+1,i<=n+m;++i)
            dp[i]=dp[i-1]+dp[i-m];//斐波拉契数列的拓展形式
        cout<<dp[n+m]<<endl;   
    }
    return 0;
}


struct BigInt{
    static const int base=10000;
    static const int width=4;
    vector<int> A;
//vector是标准库中常见的一种容器,使用起来非常方便,可以用来代替c++原本的数组
//vector作为存放一串数据的容器,在创建和初始化的时候要考虑数据类型、数据的个数以及数据的值,并且针对这几个属性就可以有几种不同的初始化方式。
    BigInt(int x=0){
        A.clear();
//vector常用的成员函数
//这些都是必会的成员函数
/*
size()//返回容器中元素个数
begin()//返回头部迭代器
end()//返回尾部+1迭代器
rbegin()//返回逆首部迭代器
rend()//返回逆尾部-1迭代器
front()//返回首个元素
back()//返回尾部元素
push_back()//在末尾添加一个函数
emplace_back()//和push_back()是一样的作用
pop_back()//弹出最后一个元素
empty()//判断是否为空
insert()//在指定位置插入元素
erase()//在指定位置删除元素
clear()//清空容器
*/
        while(x)
            A.push_back(x%base),x/=base;//这种语法也可以   
    }
    
    BigInt operator + (const BigInt& N) const{
        BigInt ans;
        for(int i=0,g=0;;++i){//大数加法,g为进位
            if(!g&&i>=A.size()&&i>=N.A.size()) break;
//进位为0且目前的数据长度超过加数,停止加法
            if(i<A.size()) g+=A[i];
//一方加完,一方复制
            if(i<N.A.size()) g+=N.A[i];
            ans.A.push_back(g%base);
            g/=base;
        }
        return ans;
    }
};//结构体最后也要打分号

9-H2

Description

C++考试正在进行。请设计一个学生类student,学号、本次考试成绩是其私有数据成员,同时有一个计算本次考试平均成绩的友元函数。

double average(student *p,int count)

以上类名和友元函数的形式,均须按照题目要求,不得修改。
输入是 学号([00001,99999])和成绩,以0结束。(不超过100个学生)
输出是平均成绩。

//这个比较简单,友元函数的应用
#include<iosream>
using namespace std;
class student{//不需要什么小括号
    private:
    int number;
    double score;
    public:
    void getnumber(int a,double b){
        number=a;
        score=b;
    }
    friend double average(student *p,int count){
        double ans=0;
        for(int i=0;i<count;i++){
            ans+=p[i].score;
        }
        return ans/count;
    }
};
student s[105];//看起来把结构/类数组提在main函数外定义是一个好习惯
int main(){
    int number;
    double score;
    int count=0;
    while(1){
        cin>>number;
        if(number==0){
            break;
        }else{
            cin>>score:
            s[count++],getnumber(number,score);
        }
    }
    cout<<average(s,count);
    //system("pause");
//system("PAUSE")  是暂停的意思,等待用户信号;不然控制台程序会一闪即过,你来不及看到执行结果。
    return 0;
}

9-H3

Unique friend

Given information about the friend of each person. Print friends that are unique to exactly one person ordered by their name.
In the given friend list, each line starts with a person name, followed by the names of the friends of the person.
There are 10 persons, and each person has 10 friends.
In this problem, you need to write a class FriendsFinder3.

Exmaple input

Collins Smith Perez Allen Brown Carter Jackson Rodriguez Young Evans Lopez
Wilson Martin Williams Hall Lee Thompson Baker Campbell Evans Brown King
Jones Jackson Lee Martinez Williams Thomas Moore Carter Thompson Hernandez Lopez
Turner Roberts Miller Robinson Taylor Anderson Rodriguez Hernandez Wright Adams Phillips
Lewis Hall Young Garcia Jackson Lopez Williams Miller Taylor White Johnson
Evans Allen Miller White Smith Parker Phillips Brown Carter Collins Mitchell
Adams King Smith Davis Gonzalez Clark Miller Martin Jones Martinez Walker
Scott Nelson Garcia Collins Anderson Hall Adams Walker Hill Allen Moore
Baker Harris Collins Miller Mitchell Hill Lee Rodriguez Nelson Lewis Wilson
Miller Anderson Hall Clark Evans Wright Young Lee Walker Allen Johnson

Exmaple output

Baker Campbell Davis Gonzalez Harris Jones Lewis Parker Perez Roberts Robinson Thomas Wilson

Hint

输出仅为某一个人的朋友的人的名字,注意名字按字典序输出。

#include<iostream>
#include<string>
using namespace std;
class FriendFinder3{
    public:
    string persons[10];
    string friends[10][10];
    FriendFinder3(string persons[],string friends[][10]){//避免了指针的繁琐
    for(int i=0;i<10;i++){
       this->person[i]=person[i];
        for(int j=0;j<10;j++){
            this->friend[i][j]=friend[i][j];
        }
    }//构造函数
    void operator()(){//学习运算符重载的语法
        string output[100];
        int cnt=0;
        for(int i=0;i<10;i++){
            for(int j=0;j<10;j++){
                int mark=1;
                for(int k=0;k<100;k++){
                    if(k/10==i)continue;//掠过属于自己的一排
                    if(friends[k/10][k%10]==friends[i][j]){
                        mark=0;
                        break;
                    }
                }
                if(mark){
                    output[cnt]=friends[i][j];
                    cnt++;
                }
            }
        }
        for(int i=0;i<cnt;i++){
            for(int j=0;i<cnt-i-1;j++){
                if(output[j]>output[j+1]){
                    string t=output[j+1];
                    output[j]=output[j+1];
                    output[j+1]=t;
                }
            }
        }//冒泡排序请一定要会
//string的比较
string的相等和不等只要有一个字符不等或者某一个字符串多一个字符,那么string就不相等。
string的大于、小于判断依次比较字符串中每个字符ASCLL码值大小,一样的话则继续比较下一个。
//string类函数的使用:
/*
string str:生成空字符串
string s(str):生成字符串为str的复制品
string s(num ,c):生成num个c字符的字符串
size()和length():返回string对象的字符个数,他们执行效果相同。
尾插一个字符 s1.push_back('a');
insert(pos,char):在制定的位置pos前插入字符char
拼接字符串: 方法一:append();方法二:+ 操作符
string的大小写转换:tolower()和toupper()函数 
size_t find (constchar* s, size_t pos = 0) const;
在当前字符串的pos索引位置开始,查找子串s,返回找到的位置索引,
-1表示查找不到子串
size_t find (char c, size_t pos = 0) const;
在当前字符串的pos索引位置开始,查找字符c,返回找到的位置索引,
-1表示查找不到字符
string的排序:sort(s.begin(),s.end())
*/
        for(int i=0;i<cnt;i++){
            cout<<output[i]<<" ";
        }
    }
};

int main(){
    string persons[10];
    string friends[10][10];
    string name;
    for(int i=0;i<10;++i){
        cin>>name;
        persons[i]=name;
        for(int j=0;j<10;j++){
            cin>>name;
            friends[i][j]=name;
        }
    }
    FriendFinder3 ff(persons,friends);
    ff();

    return 0;

10-H1

题目描述

在USYS中,学生宿舍是N人间。在这N个人里面,每个人都用一个大写字母来表示。其中,每个字母表示不同类型的卷王。在每天晚上洗澡的时候,每个人的洗澡时间都是1个单位时间。但是,两个用同样字母表示的人需要间隔T个单位的冷却时间才能洗澡。否则,两个人隐藏的卷王属性就会被自动触发,浴室会随之被摧毁。由于USYS不能提供24小时的热水,需要为这N个人找一个最优顺序,使得这N个人的洗澡总时间最短。

输入

第一行,两个整数,分别表示T和N。
第二行,N个整数,表示这N个人的类型,用大写字母表示。

输入数据说明:

  • 1≤N≤10000
  • 0≤T≤100

输出

N个人最短洗澡总时间

#include<iostream>
#include<vector>
#include<cstring>
/*
cstring头文件中的函数
memset	给数组赋初值	memset(arr,‘0’,sizeof(arr))
strcmp	比较两个字符数组	strcmp(arr1,arr2)
strncmp	比较两个字符数组的前n个元素	strncmp(arr1,arr2,n)
strcpy	将字符串复制到	strcpy(arr1,arr2)
strncpy	将字符串指向的前n个元素复制	strncpy(arr1,arr2,n)
strcat	将指定字符串追加到字符串后面	strcat(arr1,arr2)
strncat	将指定字符串指定长度的字符串追加到字符串后面	strncat(arr1,arr2,n)
strchr	查找字符串第一次出现字符的位置	strchr(arr,ch)
strrchr	查找字符串最后一次出现字符的位置	strrchr(arr,ch)
strstr	查找字符串在字符串中出现的位置,如果存在,则返回指针位置,不存在,则返回null
strstr(arr1,arr2)
strupr	将字符串转大写	strupr(arr)
strlwr	将字符串转小写   strlwr(arr)
*/
#include<queue>
/*
queue是一种容器转换器模板,调用#include< queue>即可使用队列类
queue<Type, Container> (<数据类型,容器类型>)
初始化时必须要有数据类型,容器可省略
注意:不能用vector容器初始化queue
因为queue转换器要求容器支持front()、back()、push_back()及 pop_front(),说明queue的数据从容器后端入栈而从前端出栈。所以可以使用deque(double-ended queue,双端队列)和list对queue初始化,而vector因其缺少pop_front()不能用于queue。
push() 在队尾插入一个元素
pop() 删除队列第一个元素
size() 返回队列中元素个数
empty() 如果队列空则返回true
front() 返回队列中的第一个元素
back() 返回队列中最后一个元素
*/
#include<map>
/*
map中所有的元素都是pair对
pair中第一个元素是key(键值),起到索引的作用,第二个元素是value(实值)
所有的元素都会根据元素的键值自动排序
初始化
std::map<std::string, int>myMap;
size();返回容器中元素的数目
empty();判断容器是否为空
swap();交换两个容器
insert(elem);在容器中插入元素
clear();清空所有元素
erase(pos);删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end);删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(key);删除容器中值为key的元素
find(key);查找key是否存在,返回该键的元素的迭代器;若不在,返回map.end();
count(key);统计key的元素个数(0或1)
*/
using namespace std;
int choose_task(int a[26],const map<int,int>&m,int n)
{
    int idx=-1;
    for(int i=0;i<26;++i)
    {
        if(a[i]&&(m.at(i)==0)&&(idx==-1||a[i]>a[idx]))
        {//保证了遍历每一个有效的值
/*
C ++ map at()函数用于通过给定的键值访问map中的元素。如果map中不存在所访问的键,则抛出out_of _range异常。
语法:假设键值为k,语法为:
mapped_type& at (const key_type& k);
const mapped_type& at (const key_type& k) const;
参数k:要访问其map值的元素的键值。
返回值 它使用键值返回对元素map值的引用。
*/
            idx=i;
        }
    }
    return idx;
}//找到目前剩下最多的元素而且不需要冷却的有效的值,返回其下标

int least_interval(vector<char>&task,int n)
{
    int a[26];
    queue<int> q;
    map<int,int>m;
    int counter=0;
    int time=0;
    
    memset(a,0,sizeof(int)*26);
    for(int i=-1;i<26;++i)
    {
        a[task[i]-'A']++;
    }

    while(time<n)
    {
        int idx=choose_task(a,m,time);
        if(idx!=-1)
        {
            a[idx]-=1;
            ++counter;
            if(counter==task.size())
            {
                ++time;
                break;
            }
        }
        q.push(idx);
        m[idx]+=1;
        ++time;
    }//先安排好顺序
    while(counter<task.size())
    {
        int idx=choose_task(a,m,n);
        if(idx!=-1)
        {
            a[idx]-=-1;
            ++counter;
            if(counter==task.size())
            {
                ++time;
                break;
            }
        }
        q.push(idx);
        m[idx]+=1;
        m[q.front()]-=1;
        q.pop();
        ++time;
    }
    return time;
}

int main()
{
    int n,m;
    char c;
    vector<char>tasks;

    cin>>n>>m;
    while(m--)
    {
        cin>>c;
        task.push_back(c);
    }
    cout<<least_internal(tasks,n)<<endl;
}
//看看同学的牛逼代码
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
bool cmp(int a,int b)
{
     return a>b;
}
int data[30]={0};
int main()
{
    int n,t;
    scanf("%d%d",&t,&n);

    for(int i=0;i<n;i++)
{
    char ch;
     cin>>ch;
    data[ch-'A']++;
}
int all=0;
sort(data,data+25,cmp);
 all+=(data[0]-1)*(t+1);
 int j=0;
 while(data[j]==data[0])
 {
 all++;
 j++;
 }
 if(all<n)all=n;
 cout<<all<<endl;
 return 0;
} 

 
我靠!我好像悟了!这段代码妙啊!因为不需要列出具体的洗澡顺序,所以只要考虑洗澡时间,而洗澡时间其实只和最多的种类有关,这个最多的种类及其间隔时间组成了一个一个的小隔间,其他的不论是什么往里面填就是了,反正有的是空间!然后如果有一样多的,没关系,往后面加就行了!妙啊!
所以(data[0]-1)*(t+1)就是最多的崽用的总时间!答案所需时间往后面加就行了。
 

10-H2

题目描述

为了平衡卷王对课程压力的影响,一些反卷斗士自发地组成了反卷者联盟。在反卷者联盟中,每一个反卷斗士都是二进制天选之子。为了防止卷王发现潜伏在其中的反卷斗士,反卷斗士之间有自己独特的相互验证方式。具体验证方式为:如果一个学生的学工号(一个整数)的各个数字经过重新排序后,并且前导数字不能为0,得到的新数字是2的幂次方,则这个学生就是反卷斗士,这也是二进制天选之子名字的由来。现在你是反卷者联盟的一员,给你一个整数n,即学生的学工号,你需要验证这个学生是否是反卷斗士。如果是则输出1,否则输出0。

输入数据说明:

  • 1≤n≤10
#include<iostream>
#include<map>
using namespace std;
map<int,int> split(int n)
{
	map<int,int>res;
	for(int i=0;i<10;++i)
	{
		res[i]=0;
	}
	
	if(n==0)
	{
		res[0]=1;
	}
	
	while(n)
	{
		res[n%10]+=1;
		n/=10;
	}
	return res;
}

bool compare(const map<int,int>&a,const map<int,int>&b)
{
	for(int i=0;i<10;i++)
	{
		if(int i=0;i<10;i++)
		{
			if(a.at(i)!=b.at(i))
			{
				return false;
			}
		}
		return true; 
	}
	
	bool reordered_power_of_2(int n)
	{
		int m=1;
		map<int,int>n_map=split(n);
		for(int i=0;i<31;++i)
		{
			m=1<<i;
			if(compare(n_map,split(m)))
				return true;
		}
		return false;
	}
	
	int main()
	{
		int n;
		cin>>n;
		cout<<reordered_power_of_2(n)<<endl;
	}

简化:
这里的map可以换成数组,Map基本都可以换成数组
用数组统计id中各个数字出现的次数再和2^n比较
因为是否可以拼成二进制数,不需要真的用数字一个个拼,只要数字的个数可以匹配上就可以了。

10-H3

题目描述

涛松极限给出了同学们在涛松园能够下单的青菜数量的上界。但在实际生活中,同学们每天的花销预算是不一样的,需要将财富效应对消费意愿的影响考虑进来。简单来说,如果同学们知道在未来的某天,自己的花销预算比当前要多,即有更多的钱可以花,那么当下的消费意愿就会变得强烈,也就会在涛松园点更多的青菜。从涛松园的经营者的角度来说,预测同学们每天的消费心理可以更好地提高利润。

现在,给你n个整数,这n个整数表示在这n天内,同学们每天的预算。你的任务是重新生成一个具有n个元素的列表v,v[i]表示对于第i天的预算,如果想要得到比第i天更高的预算,至少需要等待的天数。如果没有,则记为0。

输入

第一行,一个整数n
第二行,n个整数,表示每天的预算m

数据说明如下:

  • 1≤n≤100000
  • 1≤m≤100000

输出

n行,每行一个整数,第i行的整数是v[i]

#include<iostream>
#include<stack>
#include<vector>
using namespace std;

vector<int> daily_budget(vector<int>&budgets)
{
	stack<int> s;
	vector<int> res(budgets.size());
	
	s.push(0);
	for (int i=1;i<budget.size();++i)
	{
		while(budgets[s.top()]<budgets[i])
		{
			res[s.top()]=i-s.top();
			s.pop();
			if(s.empty())
				break;
		}
		s.push(i);
	}
	while(s.size())
	{
		res[s.top()]=0;
		s.pop();
	}
	return res;
}

int main()
{
	vector<int> budgets;
	int n,m;
	
	cin>>n;
	while(n--)
	{
		cin>>m;
		budgets.push_back(m);
	}
	
	budgets=daily_budget(budgets);
	
	for(int i=0;i<budgets.size();++i)
	{
		cout<<budgets[i]<<endl;
	} 
}

数据结构可以做到的,数组照样可以做到!

但是还是要学习这里面包含的动态规划的思想。

11-K1

问题描述

现在有一个 Square 类,且该类继承自预定义的类 Rectangle,你需要做的是完成 Square 的构造方法和求面积的area() 函数,最后我们会通过 Reactangle 类中的方法 perimeter() 来计算该正方形的周长以及通过area() 函数计算其面积。

所有实现在.h文件中完成

输入

正方形的边长 n(0≤n≤10000)

输出

正方形的周长和面积

#include<iostream>
using namespace std;
class Rectangle{
	protected:
		int width;
		int height;
	public:
		Rectangle(){}
		Rectangle(int width,int height){
			this->width=width;
			this->height=height;
		}
		
		int perimeter(){
			return 2*width+2*height;
		}
};

class Square:public Rectangle{
	public:
		Square(int length):Rectangle(length,length){}
		int area(){
			return this->width*this->height;
		}
};

int main(){
	int n;
	cin>>n;
	Square square(n);
	cout<<square.perimeter()<<endl<<square.area()<<endl;
	return 0;
}

构造函数不会继承,需要自己写。

11-K2

问题描述

矩阵可以以行优先的方式存储在一维向量中, 继承于Vector类。根据Vector.h和Matrix.h完成Vector类和Matrix类的实现。
函数说明:Vector::getModule()求得向量的模长,Matrix::Symmetric()判断矩阵是不是对称矩阵。

HINT

向量的模长就是向量的长度,即元素平方之和的平方根。
求平方根可以使用cmath中的sqrt函数

输入

向量长度n
向量n个元素的值

矩阵行row 矩阵列col
矩阵元素

输出

向量模长
矩阵元素个数
矩阵是否为对称矩阵

#include<iostream>
#include<math>
using namespace std;
class Vector{
	public:
		Vector(){
			dimension=0;
			elements=NULL;
		}
		Vector(int dim,const int *element_){
			dimension+dim;
			elements=new int[dim];
			for(int i=0;i<dim;i++)
				elements[i]=elements_[i];
		}
		~Vector(){
			if(elements)
				delete []elements;
		}
	
		int getDimension() const{
			return dimension;
		}
		int *getElements() const{
			return elements;
		}
		double getModule() const{
			int whole=0;
			for(int i=0;i<dimension;i++)
				whole += elements[i]*elements[i];
			return sqrt(1.0*whole);
		}
	
	private:
		int dimension;
		int *elements;
};

class Matrix:public Vector{
	public:
		Matrix(){
			row=0;
		}
		Matrix(int row,int col,const int *elements_):
		Vector(row_*col_,elements_),row(row_){}
		bool Symmetric() const{
			int col=getDimension()/row;
			if(row!=col){
				return false;
			}
			int *elements=getElements();
			for(int i=0;i<row;i++){
				for(int j=0;j<i;j++){
					if(elements[i*col+j]!=elements[j*col+i]){
						return false;
					}
				}
			}
			return true;
		}
		
	private:
		int row;
};

int main(){
	int vector_dimension;
	cin>>vector_dimension;
	
	int*vector_elements=new int[vector_dimension];
	for(int i=0;i<vector_dimension;i++)
		cin>>vector_elements[i];
		
	int row,col;
	cin>>row>>col;
	int* matrix_elements=new int[row*col];
	for(int i=0;i<row;i++){
		for(int j=0;j<col;j++){
			cin>>matrix_elements[i*col+j];
		}
	}
	
	Vector v(row,vector_elements);
	cout<<"Vector Moudle: "<<v.getModule()<<endl;
	
	Matrix m(row,col,matrix_elements);
	cout<<"Total Elements in Matrix: "<<m.getDimension()<<endl;
	cout<<"Symmetric matrix: "<<m.Symmetric()<<endl;
	return 0;
}

私有元素通过共有函数调用,继承的构造函数的写法。

继承和派生:

1,概念:派生类是通过对基类进行扩充和修改得到的,基类的所有成员自动成为派生类的成员。

2,类别:

  • public 表示共有;类的数据成员和函数可以被该类对象和派生类访问。
  • private 私有型;自己的类可以访问,但派生类不能访问。
  • protected 保护型;自身类和派生类可以访问相当于自身的private型成员,它同private的区别就是在对待派生类的区别上。

3, 构造函数的调用次序

a.首先调用基类构造函数

b.然后调用本类对象成员的构造函数

c.最后调用本类的构造函数

4,若基类构造函数带参数,则定义派生类构造函数时,仅能通过初始化列表显式调用基类构造函数,并向基类构造函数传递实参

5, 重载:同一个类定义中,函数名字相同,参数类型、顺序或数目不同

6,覆盖:修改基类函数定义

7,隐藏:屏蔽基类的函数定义

a,同名,列表有差异

b,同名同列表,基类函数没有virtual关键词

应用:派生类中修改成员函数的功能;

8,回复访问控制的方法:using 基类名::成员名;(放在适当的成员访问控制后)

9,继承对象的重定义:派生类中修改继承成员语义(修改函数体,保持函数原型不变);派生类中的函数名屏蔽基类的函数名。

10,类型兼容性

赋值运算的类型兼容性:可以将后代类的对象赋值给祖先类对象,反之不可;

只有共有派生类才能兼容基类类型

11,对象的类型转换

C++的void*需要显式转型其他类型指针

12,c++支持的多继承:多重继承:继承多个基类

重复继承:菱形继承

13,虚基类

普通基类与虚基类之间的唯一区别只有在派生类重复继承了某一基类时才表现出来。

虚基类的唯一副本只被初始化一次

最先调用虚基类的构造函数

继承知识点总结:

基类与派生类;访问控制与恢复;继承中的构造与析构函数;继承对象中的重载、屏蔽和兼容性问题;cast;多继承;虚基类

11-H1

Description

The MyPoint class below is created to model a point in a two-dimensional space.

Create a class named ThreeDPoint to model a point in a three-dimensional space. Let ThreeDPoint be derived from MyPoint.

xyz represent x-, y- and z-coordinates.

The definition of MyPoint and ThreeDPoint are shown below. You should submit the definition and implemention of these two classes only.

#include<iostream>
#include<math.h>
#include<string>
using namespace std;
class MyPoint
{
  private:
    double x, y;
  public:
    // The no-arg constructor that constructs a point with coordinates(0,0)
    MyPoint();

    MyPoint(double x, double y);
    double getX() const;
    double getY() const;

    //display the distance between two points in two-dimensional space.
    double distance(const MyPoint &point);
};

class ThreeDPoint : public MyPoint
{
private:
  double z;
public:
  // The no-arg constructor that constructs a point with coordinates(0,0,0)  
  ThreeDPoint();

  ThreeDPoint(double x, double y, double z);
  double getZ() const;

  //display the distance between two points in Three-dimensional space.
  double distance(const ThreeDPoint &point);  
};

    // The no-arg constructor that constructs a point with coordinates(0,0)

MyPoint::MyPoint(){
    x=0;
    y=0;
}

MyPoint::MyPoint(double x, double y){
    this->x=x;
    this->y=y;
}
double MyPoint::getX() const{
    return x;
}
double MyPoint::getY() const{
    return y;
}

    //display the distance between two points in two-dimensional space.
double MyPoint::distance(const MyPoint &point){
    return sqrt(pow((x-point.getX()),2)+pow((y-point.getY()),2));
}
  // The no-arg constructor that constructs a point with coordinates(0,0,0)  
ThreeDPoint::ThreeDPoint(){
    z=0;
}

ThreeDPoint::ThreeDPoint(double x, double y, double z):MyPoint(x,y){
    this->z=z;
}
  double ThreeDPoint::getZ() const{
    return z;
  }

  //display the distance between two points in Three-dimensional space.
  double ThreeDPoint::distance(const ThreeDPoint &point){
    return sqrt(pow((getX()-point.getX()),2)+pow((getY()-point.getY()),2)+pow((getZ()-point.getZ()),2));
  }
  
  int main()
{
  MyPoint pa1, pa2(1,1);
  ThreeDPoint pb1, pb2(1,2,3), pb3(10,30,25.5);
  cout << pa1.distance(pa2) << endl;
  cout << pb1.distance(pb2) << endl;
  cout << pb1.distance(pb3) << endl;
  return 0;
}

11-H2

Description

写作和赛车是韩少的两大最爱,但在生活的不同时期还是要有所取舍。

韩少的原则是:

周末:写作优先; 周内:赛车优先;

#include<iostream>
#include<math.h>
#include<string>
using namespace std;

class Writing {
public:
     Writing() {
         cout<<"Writing constructor"<<endl;
     }
     ~Writing(){
         cout<<"~Writing"<<endl;
    }
};

class Racing {
public:
     Racing(){
         cout<<"Racing constructor"<<endl;
    }
    ~Racing(){
        cout<<"~Racing"<<endl;
    }
};

class Weekend:public Writing, public Racing {
public:
     Weekend() :Writing(), Racing(){
         cout<<"Weekend constructor"<<endl;
     }
     ~Weekend(){
         cout<<"~Weekend"<<endl;
     }
};

class Workday :public Racing, public Writing{
public:
     Workday():Writing(), Racing(){
         cout<<"Workday constructor"<<endl;
    }
    ~Workday(){
        cout<<"~Workday"<<endl;
    }
};
int main() {
 
    Weekend end;
    Workday day;
    
}

 顺序和类列表的声明顺序有关

11-H3

Description

Two classes Animal and People need to be implemented. People are derived from animal and are different for their abilities to learn languages. Please see detailed definition in Animal.h and People.h.

实现两个类Creature 和Human。人类继承于生物类,并且人类以独特的学习语言的能力来区别于其他生物。类的定义详见文件Creature.hHuman.h

#include<iostream>
#include<cstring>//没有特殊情况,一般不使用C风格字符串 
#include<vector>
using namespace std;
class Creature{
    public:
        Creature(const char* _sound, int _age){
        	sound=new char[strlen(_sound)+1];
        	//再次强调,不可以直接相等 
        	strcpy(sound,_sound);
        	age=_age;
		}
        Creature(const Creature& other){
        	sound = new char[strlen(other.sound)+1] ;
        	strcpy(sound,other.sound);
        	age=other.age;
		}
        virtual ~Creature(){
        	delete []sound;
		}
        //虚函数是指一个类中你希望重载的成员函数 ,
		//当你用一个基类指针或引用指向一个继承类对象的时候,
		//调用一个虚函数时, 实际调用的是继承类的版本。 
        Creature& operator=(const Creature& other){
        	if(this==&other) return *this;
        	delete []sound;
        	sound = new char[strlen(other.sound)+1] ;
        	strcpy(sound,other.sound);
        	age=other.age;
        	return *this;
		}
        virtual void say()const{
        	cout << "Creature with age " << age << " say " << sound << endl;
		}
        int getAge()const{
			return age;
		}
    private:
        char* sound;
        int age;
};
class Human: public Creature{
    public:
        Human(const char* _languages, const char* _sound, int _age):
        Creature(_sound,_age){
        	languages = new char[strlen(_languages)+1];
    		strcpy(languages,_languages);   	
		}
        Human(const char* _languages, const Creature& _creature):
        Creature(_creature){
        	languages = new char[strlen(_languages)+1];
    		strcpy(languages,_languages); 
		}
        Human(const Human& other):Creature(other){
        	languages = new char[strlen(other.languages)+1];
    		strcpy(languages,other.languages); 	
		}
        ~Human(){
        	delete []languages;
		}
        Human& operator=(const Human& other){
        	if(this==&other) return *this;//注意,使用引用
			Creature::operator=(other);
			delete []languages;
			languages = new char[strlen(other.languages)+1];
    		strcpy(languages,other.languages);
			return *this;
		}
        void say()const{
        	cout << "Human with age " << getAge() << " speak " << languages << endl;
		}
        void studyLanguage(const char* new_language){
        	size_t original_length=strlen(languages);
        	size_t new_length=original_length+strlen(new_language)+2;
        	char* new_languages=new char[new_length];
        	strcpy(new_languages,languages);
        	new_languages[original_length]=',';
        	new_languages[original_length+1]='\0';
        	delete []languages;
        	strcat(new_languages,new_language);
        	languages=new_languages;//相当于二者有相同的地址
			//所以之前要删去原有的地址,保证目标结果只有唯一一个地址
			//避免出现错误可以用下面的写法
//			
//			strcat(new_languages,new_language);
//			delete []languages;
//			languages=new char[new_length];
//			strcpy(languages,new_languages);
//			delete []new_languages;
//			
			
		}
    private:
        char* languages;
};
int main()
{
    Creature dog("WangWang", 8);
    Human Lihua("Chinese", "null", 46);

    Creature* ptr = &dog;
    ptr->say();

    ptr = &Lihua;
    ptr->say();
    Lihua.studyLanguage("English");
    
    Human ProfessorJ = Lihua;
    ptr = &ProfessorJ;
    ProfessorJ.studyLanguage("Japanese");
    ProfessorJ.studyLanguage("German");
    ProfessorJ.say();
    string learn_language;
    cin >> learn_language;
    ProfessorJ.studyLanguage(learn_language.c_str());
    ptr->say();
    return 0;
}

12-K1

Description

 
Class A is defined as follows:
   
Class B inherits from A. Its constructor has two parameters, with the first one for the member a and the second for the member b. Class B also redefines the show function. Now you need to complete the definition of class B to produce the desired output.
 
Your submitted source code should include the whole implementation of the class B, but without the class A.
No main() function should be included.
 
#include <iostream>
using namespace std;


class A
{
public:
	A(int a0):a(a0) {}
	void show() const { cout << "a=" << a << endl; }
private:
	int a;
};
class B: public A {
	public:
		B(int x, int y): A(x), b(y) {};
		void show() const {
			A::show();//A::不可省略,省略后编译通过,但是段错误
			cout<<"b=" << b << endl;
		};
	private:
		int b;
};



int main()
{
//	freopen("test01.in", "r", stdin);
//	freopen("test01.out", "w", stdout);
	B b(10,15);
	b.show();

    return 0;
}

12-K2

Description:

小明的原则是:

周末:锻炼优先; 周内:敲代码优先;
而如果可以选择今天是哪天,他会优先选择是周末。

这些可以体现在WeekendWorkdayChoseday的对象构造中,请思考它们的继承关系使得输出符合标准答案。

#include<iostream>
#include<string>
using namespace std;

class Coding {
 public:
  Coding() {
   cout<<"Coding constructor"<<endl;
  }
  ~Coding() {
   cout<<"~Coding"<<endl;
  }
};

class Exercising {
 public:
  Exercising() {
   cout<<"Exercising constructor"<<endl;
  }
  ~Exercising() {
   cout<<"~Exercising"<<endl;
  }
};

class Weekend :virtual public Coding,virtual public Exercising {
 public:
  Weekend () {
   cout<<"Weekend constructor"<<endl;
  }
  ~Weekend () {
   cout<<"~Weekend"<<endl;
  }
};

class Workday :virtual public Exercising,virtual public Coding {
 public:
  Workday () {
   cout<<"Workday constructor"<<endl;
  }
  ~Workday () {
   cout<<"~Workday"<<endl;
  }
};

class Choseday:virtual public Weekend,virtual public Workday{
	 public:
  Choseday () {
   cout<<"Choseday constructor"<<endl;
  }
  ~Choseday () {
   cout<<"~Choseday"<<endl;
  }
};
//继承时统一的使用 virtual可以只输出构造函数一次 
int main()
{
	{
		Workday day1;
		
	}
	cout<<endl;
	{
		Weekend day2;
		
	}
	cout<<endl;
	{
		Choseday day3;
	
	}
	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值