主要整理了第一次 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; | |
} |
错误原因:
- 未区分 0 与 '0'
- 没有考虑到原来数字末尾有 0 的情况
- 没有考虑到数字 0 的特殊情况
- 因为题目要求倒序,所以在代码逻辑中有一部分比较绕(就是到底哪边是大的),然后被绕进去了,导致判断越界错误
- 可以略略记忆一下 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 中的一些类型转换
Convert string to integer (function template)
Convert string to long int (function template)
Convert string to unsigned integer (function template)
Convert string to long long (function template)
Convert string to unsigned long long (function template)
Convert string to float (function template)
Convert string to double (function template)
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 is10
, not0
.
// 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 中不懂的名词(孩子一无所知