主要整理了第一次 cpp 作业的问题和想法,以及一些函数和 io。

# Lecture04

# 课程

# Imperative Programming Paradigm

structure programming

单入口单出口

Object-Oriented Programming

并没有改变 imperative programming 状态转换的思想

# Declarative Programming Paradigm

Functional Programming

Logical Programming

# 作业

# 作业 1 复盘:binary2decimal

#include <iostream>
#include <string>
using namespace std;
int pow(int a, int b){
  if(b == 0){
    return 1;
  }
// 一开始没有注意到边界条件,如果 b 是 0 的话,ans 为 a 不是 1,会导致返回值的错误。
  int ans = a;
  for(int i = 1; i < b; i++){
    ans = ans * a;
  }
  return ans;
}
int main(){
  string s;
  cin >> s;
  int len = s.length();
  int ans = 0;
  int cur = 0;
  for (int i = 0; i < len ; i++) {
    cout << "str:" << s.substr(i,i+1) << endl;
// 问题出在这里,弄错了 substr 的参数
    cur = stoi(s.substr(i, i + 1));
    cout << cur << endl;
    ans += cur * pow(2, len - 1 - i);
  }
  cout << ans << endl;
  cout << len;
  return 0;
}
/**
输入:1001
**/
/**
输出:str:1
1
str:00
0
str:01
1
str:1
1
**/

# 作业 3:完成汉诺塔的最小移动次数

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
int main(){
  int n;
  cin >> n;
  int ans = 7;
  if (n <= 0) return 0;
  if (n == 1) cout << 1;
  if (n == 2) cout << 3;
  n -= 3;
  while(n--){
    ans = ans * 2 + 1;
  }
  cout << ans;
  return 0;
}

错误原因:超时,建议用通项解决

//
// Created by QUAS on 2022/9/20.
//
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
int pow(int a, int b){
  int ans = a;
  if(b == 0) return 1;
  b--;
  while(b--){
    ans *= a;
  }
  return ans;
}
int main(){
  int n;
  cin >> n;
  if (n <= 0) cout << 0;
  if (n == 1) cout << 1;
  if (n == 2) cout << 3;
// 错误原因,没有及时返回 return 0,也就是遗漏讨论了三种情况
// 不用打表,通项是一样的
  int ans = pow(2,n) - 1;
  cout << ans;
  return 0;
}

# 作业 4:倒数第 i 个单词的长度

#include<stdio.h>
#include<vector>
#include<string>
#include<iostream>
using namespace std;
// 学习一下 split 自制函数的写法
void split(string s, char delimiter, vector<string> &v){
  int begin,end;
  begin = 0;
  end = s.find(delimiter);
  while( end != string::npos){
    v.push_back(s.substr(begin,end-begin));
    begin = end + 1;
    end = s.find(delimiter, begin);
  }
// 不要忘记处理末尾部分
  if(begin != s.length()){
    v.push_back(s.substr(begin));
  }
}
int main(){
  string inp;
  int n;
  getline(cin,inp);
  cin >> n;
  vector<string> vec;
  split(inp,' ', vec);
  int len = vec.size();
  cout << vec.at(len-n).length();
  return 0;
}

# 作业 5:

#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(){
  int ch = 0;
  int ch_num = 0;
  int words = 0;
  int ret = 0;
  int flag = 0;
  ch = cin.get();
  while(ch != EOF){
    ch_num++;
    if(ch == 10 ) ret ++;
    if(ch>32){
      flag ++;
    }else{
      flag = 0;
    }
    if(flag == 1) words++;
    ch = cin.get();
  }
  cout << ch_num << ' '<< words << ' '<< ret << endl;
  return 0;
}

错误原因:最大的问题是出在,我没有认真看清楚题目。

实现如下功能:给出一段文本,输出这段文本包含的字符数、单词数(以空格和换行符为界)和行数。

输入描述 —— 保证输入中只会出现英文字母、空格( ' ' )和换行符( '\n'。输入保证每行末尾都有一个换行符。

输出描述 —— 字符数、单词数和行数,用空格分隔。

只会出现空格和换行符,我却企图讨论制表符等其他空字符。

要求输出字符数、单词数和行数,我却在纠结空格如何计算。

收获是明白了如何使用 cin.get () 和自制的 split

# 作业 6 复盘:找出唯一只出现一次的数(其他均出现两次)

#include <iostream>
#include <string>
using namespace std;
int main(){
  int inp = 0;
  int ans = 0;
  while((cin >> inp)){
    ans = ans ^ inp;
  }
  cout << ans << endl;
}

注意如何输入不定个数 cin>>inp,另外可以用 **^** 操作解决

# 作业 7 复盘:

INT_MAX 在 <limits.h> 头文件中

# 作业 8 复盘:

翻转数字

给定一个 int 类型的整数 N ,请你将它数字部分 翻转,正负性不变,并输出。

#include <iostream>
#include <string>
#include <vector>
#include <limits.h>
using namespace std;
int pow(int a, int b){
  if (b == 0) return 1;
  int ans = a;
  b--;
  while(b--){
    ans *= a;
  }
  return ans;
}
int main(){
  string str;
  cin >> str;
  vector<char> l;
  int negative = 0;
  // 判断是不是 0
  if(str.size()==1 || str[0] == '0'){
    cout << 0;
    return 0;
  }
  // 判断是不是负数
  if (str.at(0) == '-'){
    negative = 1;
  }
  if(!negative) l.push_back(str[0]);
  for(int i = 1; i < str.length(); i++){
    l.push_back(str[i]);
  }
  // 防止开头是 0
  for(int i = l.size()-1; i >= 0; i--){
    if(l[i]=='0'){
      l.pop_back();
    }else{
      break;
    }
  }
  // 判断数值有没有溢出
  int flag = 1;
  if(l.size()>10 || (l.size() == 10 && l[9] >= '3')){
    flag = 0;
  }
  // 计算答案
  int ans = 0;
  for(int i = 0; i < l.size(); i++){
    ans += (l[i] - '0') * pow(10, i);
  }
  if (ans < 0 ) flag = 0;
  if (ans == INT_MIN && negative == 1) flag = 1;
  if (!flag) {
    cout << -1 << endl;
  }else{
    if(negative) cout << '-';
    for(int i = l.size() -1; i >= 0; i--){
      cout << l[i];
    }
  }
  return 0;
}

错误原因:

  1. 未区分 0 与 '0'
  2. 没有考虑到原来数字末尾有 0 的情况
  3. 没有考虑到数字 0 的特殊情况
  4. 因为题目要求倒序,所以在代码逻辑中有一部分比较绕(就是到底哪边是大的),然后被绕进去了,导致判断越界错误
  5. 可以略略记忆一下 INT_MAX(2^31-1=2147483647)和 INT_MIN( -2^31=-2147483648

# 作业 9 复盘:最大单词后缀

好像没什么好复盘的(

一道很常见的简单算法题

# 作业 10 复盘:matrix 的加法、减法、乘法、卷积操作

思考如何设计并简化代码

呃呃一遍过,把输入写了一个函数,把 add、sub 和 mul 进行了合并,conv 没想出来怎么合并。

#include <iostream>
#include <vector>
using namespace std;
void inputMatrix(vector<vector<int>> &v,  int height, int width){
  v.resize(height);
  for (auto &item : v){
    item.resize(width);
    for(auto &elem : item){
      cin >> elem;
    }
  }
}
void operateMatrix(string operation, vector<vector<int>> &mat1, vector<vector<int>> &mat2){
  int ans = 0;
  if(operation == "add" || operation == "sub" || operation == "mul"){
    // 命令为 add sub mul 的情况
    if(mat1.size()!=mat2.size() || mat1[0].size() != mat2[0].size()){
      cout << "error" << endl;
      return;
    }
    for(int i = 0; i < mat1.size(); i++){
      for(int j = 0; j < mat1[0].size(); j++){
        if(operation == "add"){
          ans = mat1[i][j] + mat2[i][j];
        }else if(operation == "sub"){
          ans = mat1[i][j] - mat2[i][j];
        }else{
          ans = mat1[i][j] * mat2[i][j];
        }
        cout << ans << ' ';
      }
      cout << endl;
    }
  }else{
    // 命令为 conv 的情况
    if(mat1.size()<mat2.size() || mat1[0].size() < mat2[0].size()){
      cout << "error" << endl;
      return;
    }
    int hei = mat1.size() - mat2.size() + 1;
    int wid = mat1[0].size() - mat2[0].size() + 1;
    for(int i = 0; i < hei; i++){
      for(int j  = 0; j < wid; j++){
          for(int p = 0; p < mat2.size(); p ++){
            for(int q = 0; q < mat2[0].size(); q++){
              ans += mat1[i+p][j+q] * mat2[p][q];
            }
          }
          cout << ans << ' ';
          ans = 0;
      }
      cout << endl;
    }
  }
}
int main(){
  int n, height, width, mat1, mat2;
  string operation;
  cin >> n;
  vector<vector<vector<int>>> v;
  vector<vector<int>> ans;
  v.resize(n);
  for(int i = 0; i < n; i++){
    cin >> height;
    cin >> width;
    inputMatrix(v[i],  height, width);
  }
  cin >> n;
  while(n--){
    cin >> operation;
    cin >> mat1;
    cin >> mat2;
    operateMatrix(operation, v[mat1], v[mat2]);
  }
}

顺带贴一贴 sa 酱的 60 行代码(

#include <bitsdc++.h>
using namespace std;
int main(void)
{
    int n, m;
    int matrix[21][21][21];
    int hw[21][2];
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> hw[i][0] >> hw[i][1];
        for (int x = 0; x < hw[i][0]; x++) {
            for (int y = 0; y < hw[i][1]; y++) {
                cin >> matrix[i][x][y];
            }
        }
    }
    cin >> m;
    for (int i = 0; i < m; i++) {
        string cmd;
        int a, b;
        cin >> cmd >> a >> b;
        if (cmd != "conv") {
            if (hw[a][0] != hw[b][0] || hw[a][1] != hw[b][1]) {
                cout << "error" << endl;
                continue;
            }
            for (int x = 0; x < hw[a][0]; x++) {
                for (int y = 0; y < hw[a][1]; y++) {
                    if (cmd == "add") {
                        cout << matrix[a][x][y] + matrix[b][x][y] << " ";
                    } else if (cmd == "sub") {
                        cout << matrix[a][x][y] - matrix[b][x][y] << " ";
                    } else {
                        cout << matrix[a][x][y] * matrix[b][x][y] << " ";
                    }
                }
                cout << endl;
            }
        } else {
            if (hw[a][0] < hw[b][0] || hw[a][1] < hw[b][1]) {
                cout << "error" << endl;
                continue;
            }
            for (int x = 0; x <= hw[a][0] - hw[b][0]; x++) {
                for (int y = 0; y <= hw[a][1] - hw[b][1]; y++) {
                    int res = 0;
                    for (int j = 0; j < hw[b][0]; j++) {
                        for (int k = 0; k < hw[b][1]; k++) {
                            res += matrix[a][x + j][y + k] * matrix[b][j][k];
                        }
                    }
                    cout << res << " ";
                }
                cout << endl;
            }
        }
    }
    return 0;
}

关于我不会同时输入多个,和把某些值嵌入表达式中求解这件事

# 函数用法

# 关于 substr 的用法

string substr (size_t pos = 0, size_t len = npos) const;

# 关于 find 的用法

InputIterator find (InputIterator first, InputIterator last, const T& val);

其中,first 和 last 为输入迭代器,[first, last) 用于指定该函数的查找范围;val 为要查找的目标元素。

正因为 first 和 last 的类型为输入迭代器,因此该函数适用于所有的序列式容器。

另外,该函数会返回一个输入迭代器,当 find () 函数查找成功时,其指向的是在 [first, last) 区域内查找到的第一个目标元素;如果查找失败,则该迭代器的指向和 last 相同。

值得一提的是,find () 函数的底层实现,其实就是用 == 运算符将 val 和 [first, last) 区域内的元素逐个进行比对。这也就意味着,[first, last) 区域内的元素必须支持 == 运算符。

# 关于自制 split

void split (string s, char delimiter, vector<string> &v){
  int begin = 0;
  int end = s.find(delimiter);
  while(end != string::npos){
    v.push_back(s.substr(begin, end-begin));
    begin = end+1;
    end = s.find(delimiter, begin);
  }
  if(begin != s.length()-1){
    v.push_back(s.substr(begin,s.length()-begin));
  }
}

在我的实验(debug)中发现,如果两个字符串之间有 n(n>=1)个空格,那么 vector 会插入 n-1 个空的数据。自制 split 并不能排除掉连续的多个空格。

# 关于自制 pow

int pow(int a, int b){
  int ans = a;
  if(b == 0) return 1;
  b--;
  while(b--){
    ans *= a;
  }
  return ans;
}

今天看数据结构,递归处理貌似更快,试一下(

int pow(long x, int n){
  // 处理边界条件
  if(n == 0) return 1;
  if(n == 1) return x;
  if(n % 2 == 0) return pow(x * x, n / 2);
  else return pow(x * x, n / 2) * x;
}

# 关于 vector 构建多维数组、初始化、访问的方法

vector<vector<vector<int>>> v;
void inputMatrix(vector<vector<int>> &v,  int height, int width){
  v.resize(height);
  for (auto &item : v){
    item.resize(width);
    for(auto &elem : item){
      cin >> elem;
    }
  }
}
std::vector<std::vector<int>> vec1 = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
vector<vector<int>> vec1_2D (HEIGHT, vector<int> (WIDTH, 0));
std::vector<std::vector<std::vector<int>>> vec1_3D(HEIGHT, std::vector<std::vector<int>>(
WIDTH, std::vector<int>(DEPTH)));

讲得很清楚的链接

# 关于 cpp 中的一些类型转换

  • stoi

    Convert string to integer (function template)

  • stol

    Convert string to long int (function template)

  • stoul

    Convert string to unsigned integer (function template)

  • stoll

    Convert string to long long (function template)

  • stoull

    Convert string to unsigned long long (function template)

  • stof

    Convert string to float (function template)

  • stod

    Convert string to double (function template)

  • stold

    Convert string to long double (function template)

# stoi

int stoi (const string&  str, size_t* idx = 0, int base = 10);
int stoi (const wstring& str, size_t* idx = 0, int base = 10);
  • str

​ String object with the representation of an integral number.

  • idx

    Pointer to an object of type size_t, whose value is set by the function to position of the next character in str after the numerical value.

    This parameter can also be a null pointer, in which case it is not used.

  • base

    Numerical base (radix) that determines the valid characters and their interpretation.

    If this is 0 , the base used is determined by the format in the sequence (see strtol for details). Notice that by default this argument is 10 , not 0 .

// stoi example
#include <iostream>   // std::cout
#include <string>     // std::string, std::stoi
int main ()
{
  std::string str_dec = "2001, A Space Odyssey";
  std::string str_hex = "40c3";
  std::string str_bin = "-10010110001";
  std::string str_auto = "0x7f";
  std::string::size_type sz;   // alias of size_t
  int i_dec = std::stoi (str_dec,&sz);
  int i_hex = std::stoi (str_hex,nullptr,16);
  int i_bin = std::stoi (str_bin,nullptr,2);
  int i_auto = std::stoi (str_auto,nullptr,0);
  std::cout << str_dec << ": " << i_dec << " and [" << str_dec.substr(sz) << "]\n";
  std::cout << str_hex << ": " << i_hex << '\n';
  std::cout << str_bin << ": " << i_bin << '\n';
  std::cout << str_auto << ": " << i_auto << '\n';
  return 0;
}

# stof

float stof (const string&  str, size_t* idx = 0);float stof (const wstring& str, size_t* idx = 0);

# IO

# 关于读入 int

int n;
cin >> n;
cin >> ws;

cin >> n 不会读取‘\n' , 需要另外做处理

# 关于 ch = cin.get()的用法

int ch;
ch = cin.get();
// 这样写返回字符编码(整型),其中 32 为空格,10 为换行符,小于 32 不是一般意义上的字符。

# 关于 cin.get (ch) 的用法

char ch;
while(cin.get(ch)){
    //do something
}
// 这样写赋值给参数 ch
// 会读入空白符
属性cin.get(ch)ch = cin.get()
传递输入字符的方式赋值给参数 ch将函数返回值赋给 ch
用于字符输入时函数的返回值istream 对象(执行 bool 转换后为 true )int 类型的字符编码
到达 EOF 时函数的返回值istream 对象(执行 bool 转换后为 false )EOF

# 输入 string

string str;
// 以空白符或 EOF 为终止
cin >> str;
// 以换行符为终止
getline(cin, str);
// 以 delimiter 为终止
getline(cin, str, delimiter);

# 关于 getline 的用法

istream& getline (istream& is, string& str, char delim);	
istream& getline (istream& is, string& str);

成功读取时返回 true,错误读取时返回 false

delim 会被读取!注意区分 cin 和 getline 的用法

# todo

复习各种数据结构的基本函数

学习 template 等 cpp 中不懂的名词(孩子一无所知