目录
- [1. 数字统计(数学+模拟)](#1. 数字统计(数学+模拟))
- [2. 两个数组的交集(哈希)](#2. 两个数组的交集(哈希))
- [3. 点击消除(栈)](#3. 点击消除(栈))
- [4. 牛牛的快递(模拟)](#4. 牛牛的快递(模拟))
- [5. 最小花费爬楼梯(动态规划-线性dp)](#5. 最小花费爬楼梯(动态规划-线性dp))
- [6. 数组中两个字符串的最小距离(模拟+贪心)](#6. 数组中两个字符串的最小距离(模拟+贪心))
- [7. 简写单词(模拟)](#7. 简写单词(模拟))
- [8. dd爱框框(滑动窗口)](#8. dd爱框框(滑动窗口))
- [9. 除2!(贪心+堆)](#9. 除2!(贪心+堆))
- [10. Fibonacci数列(斐波那契数列)](#10. Fibonacci数列(斐波那契数列))
- [11. 单词搜索(搜索)](#11. 单词搜索(搜索))
- [12. 杨辉三角(动态规划)](#12. 杨辉三角(动态规划))
- [13. 游游的you(贪心 + 模拟)](#13. 游游的you(贪心 + 模拟))
- [14. 腐烂的苹果(多源 BFS)](#14. 腐烂的苹果(多源 BFS))
- [15. 孩子们的游戏(约瑟夫问题)](#15. 孩子们的游戏(约瑟夫问题))
- [16. 大数加法(高精度加法)](#16. 大数加法(高精度加法))
- [17. 链表相加(二)(链表 + 高精度加法)](#17. 链表相加(二)(链表 + 高精度加法))
- [18. 大数乘法(高精度乘法)](#18. 大数乘法(高精度乘法))
1. 数字统计(数学+模拟)

开胃小菜,依次枚举区间内的每个数,每个数判断每一位是否为2,对每个数不断%10,/10。
cpp
#include <iostream>
using namespace std;
int main()
{
int l, r;
cin >> l >> r;
int ret = 0;
for (int i = l; i <= r; i++)
{
int tmp = i;
while (tmp)
{
if (tmp % 10 == 2)
ret++;
tmp /= 10;
}
}
cout << ret << endl;
return 0;
}
2. 两个数组的交集(哈希)
遍历第一个数组,将其中⼀个数组丢进哈希表中;
遍历另⼀个数组的时候,在哈希表中看看是否存在,存在则属于交集。
cpp
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
vector<int> ret;
unordered_map<int, bool> hash;
for (auto x : nums1) {
hash[x] = true;
}
for (auto x : nums2) {
if (hash[x]) {
ret.push_back(x);
hash[x] = false;
}
}
return ret;
}
};
3. 点击消除(栈)

思路:用栈来模拟消除的过程
cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
string s, st;
cin >> s;
for (auto ch : s) {
if (st.size() && st.back() == ch) st.pop_back();
else st += ch;
}
cout << (st.size() == 0 ? "0" : st) << endl;
return 0;
}
4. 牛牛的快递(模拟)

很简单,按照题意模拟即可。
补充一个知识点,在math.h中有两个库函数ceil和floor,能对浮点数进行向上取整、向下取整。
cpp
#include <iostream>
#include <cmath>
using namespace std;
int main() {
float a;
char b;
cin >> a >> b;
int ret = 0;
if (a <= 1) {
ret += 20;
} else {
ret += 20;
a -= 1;
ret += ceil(a);
}
if (b == 'y') ret += 5;
cout << ret << endl;
return 0;
}
5. 最小花费爬楼梯(动态规划-线性dp)

简单的dp,dp[i]表示从头到达i位置台阶的最小花费。当n==1或2时,你也至少需要跨一个台阶。
cpp
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> cost(n);
for(int i = 0; i < n; i++)
{
cin >> cost[i];
}
if(n == 1)
{
cout << cost[0] << endl;
return 0;
}
if(n == 2)
{
cout << min(cost[0], cost[1]) << endl;
return 0;
}
vector<int> dp(n+1);
dp[0] = dp[1] = 0;
for(int i = 2; i <= n; i++)
{
dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);
}
cout << dp[n] << endl;
return 0;
}
6. 数组中两个字符串的最小距离(模拟+贪心)

用两个变量分别记录两个字符串最近一次出现的位置,每次判断距离是否更小。
cpp
#include<iostream>
#include<string>
using namespace std;
int main()
{
int n;
cin >> n;
string s1, s2;
cin >> s1 >> s2;
int i1 = -1, i2 = -1;
int ret = 1000000;
for (int i = 0; i < n; i++)
{
string s;
cin >> s;
if (s == s1)
{
if (i2 != -1)
{
ret = min(ret, i - i2);
}
i1 = i;
}
else if (s == s2)
{
if (i1 != -1)
{
ret = min(ret, i - i1);
}
i2 = i;
}
}
cout << ((ret == 1000000) ? -1 : ret) << endl;
return 0;
}
7. 简写单词(模拟)

最简单的一集,不说了。
cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
while (cin >> s) {
if (s[0] <= 'z' && s[0] >= 'a') cout << (char)(s[0] - 32);
else cout << s[0];
}
return 0;
}
8. dd爱框框(滑动窗口)

基础的滑动窗口题型
cpp
#include <iostream>
using namespace std;
const int N = 1e7+10;
int arr[N];
int n, x;
int main()
{
cin >> n >> x;
for(int i = 1; i <= n; i++) cin >> arr[i];
int left = 0, right = 0, sum = 0;
int retLen = N, retLeft = -1, retRight = -1;
while(right <= n)
{
sum += arr[right];
while(sum >= x)
{
if(right - left + 1 < retLen)
{
retLeft = left;
retRight = right;
retLen = right - left + 1;
}
sum -= arr[left++];
}
right++;
}
cout << retLeft << " " << retRight << endl;
return 0;
}
9. 除2!(贪心+堆)

贪心策略很好想,每次取当前最大的偶数 /= 2,用堆来维护。直到没有偶数或次数用完。
cpp
#include <iostream>
#include <queue>
using namespace std;
typedef long long LL;
LL n, k;
priority_queue<LL> heap;
int main()
{
cin >> n >> k;
LL sum = 0, x;
while(n--)
{
cin >> x;
sum += x;
if(x % 2 == 0) heap.push(x);
}
while(heap.size() && k--)
{
LL t = heap.top() / 2;
heap.pop();
sum -= t;
if(t % 2 == 0) heap.push(t);
}
cout << sum << endl;
return 0;
}
10. Fibonacci数列(斐波那契数列)

求斐波那契数列的过程中,判断一下:何时 n 会在两个斐波那契数之间,再判断那边距离更小即可。
cpp
#include <iostream>
using namespace std;
int n;
int main() {
cin >> n;
int a = 0, b = 1, c = 1;
while (n > c) {
a = b;
b = c;
c = a + b;
}
cout << min(c - n, n - b) << endl;
return 0;
}
11. 单词搜索(搜索)

一个简单的深度优先搜索题型。
cpp
class Solution {
public:
int n;
int m;
bool vis[101][101];
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
string _word;
bool exist(vector<string>& board, string word) {
n = board.size();
m = board[0].size();
_word = word;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (board[i][j] == word[0] && dfs(board, i, j, 0))
return true;
}
}
return false;
}
bool dfs(vector<string>& board, int i, int j, int pos) {
if (pos == _word.size() - 1) return true;
vis[i][j] = true;
for (int k = 0; k < 4; k++) {
int a = i + dx[k];
int b = j + dy[k];
if (a >= 0 && a < n && b >= 0 && b < m && board[a][b] == _word[pos + 1] && !vis[a][b]) {
if (dfs(board, a, b, pos + 1))
return true;
}
}
vis[i][j] = false;
return false;
}
};
12. 杨辉三角(动态规划)

简单的动态规划。
cpp
#include<iostream>
#include<vector>
#include<cstdio>
using namespace std;
int main()
{
int n;
cin >> n;
vector<vector<int>> dp(n, vector<int>(n));
for(int i = 0; i < n; i++)
{
for(int j = 0; j <= i; j++)
{
if(j == 0 || j == i) dp[i][j] = 1;
else dp[i][j] = dp[i-1][j]+dp[i-1][j-1];
printf("%5d", dp[i][j]);
}
printf("\n");
}
return 0;
}
13. 游游的you(贪心 + 模拟)

显然 you 和 oo 两个字符串不可能有字符重叠部分,但是 you 的分值更高,因此我们应该优先去拼凑 you,然后再考虑 oo。
cpp
#include<iostream>
using namespace std;
int main()
{
int q;
cin >> q;
while(q--)
{
int a, b, c;
cin >> a >> b >> c;
int ans = 0;
int min_of_y_u = min(a, c);
int num_of_o = b;
if(min_of_y_u >= num_of_o)
{
ans += 2*num_of_o;
}
else
{
ans += 2*min_of_y_u;
num_of_o -= min_of_y_u;
ans += num_of_o-1;
}
cout << ans << endl;
}
return 0;
}
14. 腐烂的苹果(多源 BFS)

这道题目是多源BFS问题。但是第一次做这道题时,笔者还没有学到这种题型,而只会BFS最短路问题。于是依此为基础,我去掉了一般的bool visited数组,而加入了一个time数组记录每个好苹果最短的腐烂时间,每次取更小时间填入。
cpp
class Solution
{
public:
int n;
int m;
int time[1001][1001] = {0};
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
int rotApple(vector<vector<int>>& grid)
{
n = grid.size();
m = grid[0].size();
int ret = 0;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(grid[i][j] == 2)
bfs(grid, i, j);
}
}
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
if(grid[i][j] == 1 && time[i][j] == 0)
{
return -1;
}
ret = max(time[i][j], ret);
}
}
return ret;
}
void bfs(vector<vector<int>>& grid, int i, int j)
{
int cur_time = 0;
queue<pair<int, int>> q;
q.push({i, j});
time[i][j] = 0;
int size = 0;
while(!q.empty())
{
size = q.size();
auto [a, b] = q.front();
q.pop();
cur_time = time[a][b];
for(int k = 0; k < 4; k++)
{
int x = a + dx[k];
int y = b + dy[k];
if(x >= 0 && x < n && y >= 0 && y < m && grid[x][y] == 1 && (time[x][y]==0 || time[x][y] > cur_time))
{
q.push({x, y});
time[x][y] = cur_time + 1;
}
}
}
}
};
15. 孩子们的游戏(约瑟夫问题)

经典的约瑟夫问题,用动态规划来解决,dp[i] 表示 i 个人玩游戏最后剩下的人的下标(从0开始)。
递推公式:dp[i] = (dp[i-1] + m) % i
递推的本质:当从 i-1 个人扩展到 i 个人时,新加入的人在下标0位置,原来的索引都向后移动 m 位,所以要在原结果上加 m 再对 i 取模。
环形队列模拟当然也能完成这种题目,不过时间复杂度略逊一筹。
cpp
class Solution {
public:
int LastRemaining_Solution(int n, int m)
{
int f = 0; // f初始相当于dp[1];
for(int i = 2; i <= n; i++)
f = (f + m) % i;
return f;
}
};
16. 大数加法(高精度加法)

模拟加法列竖式运算的过程即可,每次记录进位。
cpp
class Solution {
public:
string solve(string s, string t) {
string ret;
int flag = 0;
int i = s.size() - 1, j = t.size() - 1;
while (i >= 0 || j >= 0) {
int tmp = flag;
if (i >= 0) tmp += s[i--] - '0';
if (j >= 0) tmp += t[j--] - '0';
if (tmp >= 10) {
tmp %= 10;
flag = 1;
} else {
flag = 0;
}
ret += tmp + '0';
}
if (flag == 1) {
ret += '1';
}
reverse(ret.begin(), ret.end());
return ret;
}
};
17. 链表相加(二)(链表 + 高精度加法)

模拟高精度加法的过程,只不过是在链表中进行而已。先将链表逆序,这样从头到尾相加就是从低位到高位的过程,最后再逆序结果链表。
cpp
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) : val(x), next(nullptr) {}
* };
*/
class Solution {
public:
ListNode* revList(ListNode* head) {
if (head == nullptr || head->next == nullptr) return head;
ListNode* cur1 = head;
ListNode* cur2 = head->next;
ListNode* newhead = revList(cur2);
cur2->next = cur1;
cur1->next = nullptr;
return newhead;
}
ListNode* addInList(ListNode* head1, ListNode* head2) {
head1 = revList(head1);
head2 = revList(head2);
ListNode* newhead = new ListNode(0);
ListNode* cur1 = head1, *cur2 = head2;
ListNode* cur = newhead;
int flag = 0;
while (cur1 || cur2) {
int tmp = flag;
if (cur1) {
tmp += cur1->val;
cur1 = cur1->next;
}
if (cur2) {
tmp += cur2->val;
cur2 = cur2->next;
}
if (tmp >= 10) {
tmp %= 10;
flag = 1;
} else {
flag = 0;
}
ListNode* node = new ListNode(tmp);
cur->next = node;
cur = cur->next;
}
if (flag) {
ListNode* node = new ListNode(flag);
cur->next = node;
}
newhead->next = revList(newhead->next);
return newhead->next;
}
};
18. 大数乘法(高精度乘法)

根据列竖式运算的过程模拟即可。
但是我们可以改进一下列竖式的过程:
- 先计算无进位相乘并且相加后的结果;
- 然后再处理进位。

cpp
class Solution {
public:
string solve(string s, string t) {
if (s == "0" || t == "0") return "0";
reverse(s.begin(), s.end());
reverse(t.begin(), t.end());
int n = s.size(), m = t.size();
int i = 0, j = 0;
vector<int> tmp(m + n - 1);
for (i = 0; i < n; i++) {
for (j = 0; j < m; j++) {
tmp[i + j] += (s[i] - '0') * (t[j] - '0');
}
}
int flag = 0;
string ret;
for (i = 0; i < tmp.size(); i++) {
int t = flag;
t += tmp[i];
if (t >= 10) {
flag = t / 10;
t %= 10;
} else {
flag = 0;
}
ret += '0' + t;
}
while (flag) {
ret += '0' + flag % 10;
flag /= 10;
}
reverse(ret.begin(), ret.end());
return ret;
}
};
