目录
- [55. 小易的升级之路(数学 + 模拟)](#55. 小易的升级之路(数学 + 模拟))
- [56. 礼物的最大价值(动态规划 - 路径问题)](#56. 礼物的最大价值(动态规划 - 路径问题))
- [57. 对称之美(字符串 + 哈希)](#57. 对称之美(字符串 + 哈希))
- [58. 经此一役小红所向无敌(模拟)](#58. 经此一役小红所向无敌(模拟))
- [59. 连续子数组最大和(动态规划 - 线性dp)](#59. 连续子数组最大和(动态规划 - 线性dp))
- [60. 非对称之美(规律)](#60. 非对称之美(规律))
- [61. 爱丽丝的人偶(贪心 + 构造)](#61. 爱丽丝的人偶(贪心 + 构造))
- [62. 集合(排序)](#62. 集合(排序))
- [63. 最长回文子序列(动态规划 - 区间 dp)](#63. 最长回文子序列(动态规划 - 区间 dp))
- [64. 添加字符(字符串)](#64. 添加字符(字符串))
- [65. 数组变换(贪心 + 位运算)](#65. 数组变换(贪心 + 位运算))
- [66. 装箱问题(动态规划 - 01 背包)](#66. 装箱问题(动态规划 - 01 背包))
- [67. 打怪(模拟)](#67. 打怪(模拟))
- [68. 字符串的分类(哈希 / 排序)](#68. 字符串的分类(哈希 / 排序))
- [69. 城市群数量(并查集/搜索)](#69. 城市群数量(并查集/搜索))
- [70. 判断是不是平衡二叉树(二叉树 + 递归)](#70. 判断是不是平衡二叉树(二叉树 + 递归))
- [71. 最大子矩阵(二维前缀和)](#71. 最大子矩阵(二维前缀和))
- [72. 小葱的01串(滑动窗口)](#72. 小葱的01串(滑动窗口))
55. 小易的升级之路(数学 + 模拟)
https://www.nowcoder.com/practice/fe6c73cb899c4fe1bdd773f8d3b42c3d?tpId=122&tqId=33649&ru=/exam/oj

根据题意模拟即可
cpp
#include <iostream>
#include <vector>
using namespace std;
int gcd(int x1, int x2)
{
if(x2 == 0) return x1;
return gcd(x2, x1%x2);
}
int main()
{
int n, a;
while(cin >> n >> a)
{
while(n--)
{
int b;
cin >> b;
if(b <= a)
{
a += b;
}
else
{
a += gcd(a, b);
}
}
cout << a << endl;
}
return 0;
}
56. 礼物的最大价值(动态规划 - 路径问题)
https://www.nowcoder.com/practice/2237b401eb9347d282310fc1c3adb134?tpId=265&tqId=39288&ru=/exam/oj

简单路径类 dp 问题。
cpp
class Solution {
public:
int maxValue(vector<vector<int> >& grid)
{
int n = grid.size();
int m = grid[0].size();
vector<vector<int>> dp(n, vector<int>(m));
dp[0][0] = grid[0][0];
for(int i = 1; i < n; i++)
{
dp[i][0] = dp[i-1][0] + grid[i][0];
}
for(int j = 1; j < m; j++)
{
dp[0][j] = dp[0][j-1] + grid[0][j];
}
for(int i = 1; i < n; i++)
{
for(int j = 1; j < m; j++)
{
dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + grid[i][j];
}
}
return dp[n-1][m-1];
}
};
57. 对称之美(字符串 + 哈希)
https://ac.nowcoder.com/acm/problem/214850

左右双指针,从第一个和最后一个字符串向中间判断,看看两个字符串中有没有相同的字符。
cpp
#include <iostream>
#include <string>
#include <vector>
#include <unordered_set>
using namespace std;
void Fun()
{
int n;
cin >> n;
vector<string> v(n);
vector<unordered_set<char>> hash(n);
for (int i = 0; i < n; i++)
{
cin >> v[i];
for (char ch : v[i])
{
hash[i].insert(ch);
}
}
for (int l = 0, r = n - 1; l < r; l++, r--)
{
int flag = 0;
for (char ch : v[l])
{
if (hash[r].count(ch))
{
flag = 1;
break;
}
}
if(flag == 0)
{
cout << "No" << endl;
return;
}
}
cout << "Yes" << endl;
}
int main()
{
int t;
cin >> t;
while (t--)
{
Fun();
}
return 0;
}
58. 经此一役小红所向无敌(模拟)
https://ac.nowcoder.com/acm/problem/223985

根据题意模拟即可
cpp
#include <iostream>
using namespace std;
int main()
{
long long a, h, b, k;
cin >> a >> h >> b >> k;
long long ans = 0;
while(h > 0 && k > 0)
{
ans += a;
ans += b;
h -= b;
k -= a;
}
if(h > 0 && k <= 0)
{
ans += a * 10;
}
else if(h <= 0 && k > 0)
{
ans += b * 10;
}
cout << ans << endl;
return 0;
}
59. 连续子数组最大和(动态规划 - 线性dp)
https://www.nowcoder.com/practice/1718131e719746e9a56fb29c40cc8f95?tpId=230&tqId=39753&ru=/exam/oj

线性动态规划:
- 状态表示:dp[i] 表示:以 i 位置为结尾的所有子数组中,最大和是多少。
- 状态转移方程:dp[i] = max(dp[i - 1] + arr[i], arr[i])
cpp
#include <climits>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> nums(n);
for(int i = 0; i < n; i++)
{
cin >> nums[i];
}
vector<long long> dp(n);
// dp[i] 以i位置为结尾的所有子数组中,最大和是多少。
dp[0] = nums[0];
long long ans = dp[0];
for(int i = 1; i < n; i++)
{
dp[i] = max(dp[i-1], (long long)0) + nums[i];
ans = max(ans, dp[i]);
}
cout << ans << endl;
return 0;
}
60. 非对称之美(规律)
https://ac.nowcoder.com/acm/problem/214851

找规律:
- 若原字符串不是回文串,则最长非回文子串的长度就是原串长度;
- 若原字符串是回文串,再分两种情况:
- 若所有字符都相同,则不存在非回文子串,答案为 0;
- 若字符不全相同,则去掉首尾任一字符后,得到的长度为n-1的子串必为非回文串,答案为n-1。
cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string s;
cin >> s;
int ans = 0;
int n = s.size();
int flag = 1;
for(char ch : s)
{
if(ch != s[0])
{
flag = 0;
break;
}
}
if(flag == 1)
{
// 原字符串全为一个字符
cout << 0 << endl;
return 0;
}
for(int l = 0, r = n - 1; l < r; l++, r--)
{
if(s[l] != s[r])
{
// 原字符串本身就是非回文
cout << n << endl;
return 0;
}
}
// 原字符串是回文的
cout << n - 1 << endl;
return 0;
}
61. 爱丽丝的人偶(贪心 + 构造)
https://ac.nowcoder.com/acm/problem/213471

按照"大小大小大小"的规律安排
cpp
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> v(n);
int cur = 0;
for(int i = 1; i <= n; i++)
{
v[cur] = i;
cur += 2;
if(cur >= n) cur = 1;
}
for(auto i : v)
{
cout << i << ' ';
}
return 0;
}
62. 集合(排序)
https://www.nowcoder.com/practice/635ff765d4af45b5bf8e3756ed415792?tpId=134&tqId=33860&ru=/exam/oj

使用std::set即可
cpp
#include <iostream>
#include <set>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
int x;
set<int> s;
for (int i = 0; i < n; i++)
{
cin >> x;
s.insert(x);
}
for (int i = 0; i < m; i++)
{
cin >> x;
s.insert(x);
}
for (int x : s)
{
cout << x << " ";
}
return 0;
}
63. 最长回文子序列(动态规划 - 区间 dp)
https://www.nowcoder.com/practice/82297b13eebe4a0981dbfa53dfb181fa?tpId=230&tqId=39762&ru=/exam/oj

简单的子序列问题,区间dp
cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string s;
cin >> s;
int n = s.size();
vector<vector<int>> dp(n, vector<int>(n));
// dp[i][j]表示s[i j]中的最长回文子序列
for(int i = n-1; i >= 0; i--)
{
for(int j = i; j < n; j++)
{
if(i == j)
{
dp[i][j] = 1;
}
else if(i + 1 == j)
{
dp[i][j] = s[i] == s[j]? 2 : 1;
}
else
{
if(s[i] == s[j])
{
dp[i][j] = dp[i+1][j-1] + 2;
}
else
{
dp[i][j] = max(dp[i+1][j], dp[i][j-1]);
}
}
}
}
cout << dp[0][n-1] << endl;
return 0;
}
64. 添加字符(字符串)
https://www.nowcoder.com/questionTerminal/b2b816e20e8343b49abbaf493886ce26

枚举B串中每个长度等于A串的长度,看看这两个串同一位置上不同的字符个数。
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1, s2;
cin >> s1 >> s2;
int n = s1.size();
int m = s2.size();
int dis = m - n;
int ret = 100;
for(int i = 0; i < m-n+1; i++)
{
int i1 = 0, i2 = i;
int count = 0;
while(i1 < n)
{
if(s1[i1] != s2[i2])
{
count++;
}
i1++;
i2++;
}
ret = min(ret, count);
}
cout << ret << endl;
return 0;
}
65. 数组变换(贪心 + 位运算)
https://www.nowcoder.com/questionTerminal/c55f4f15cc3f4ff0adede7f9c69fa0c1

对于数组中的每一个数ai ,都可以表示为:
ai = k · 2t,k为奇数,t为非负整数。
那么,如果数组中的数经过若干次乘2操作后,只会改变t的大小,所以可以得出结论:如果能够变换成功,数组中的数的k值一定都相同!
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int fun(int x)
{
while(x % 2 == 0)
x /= 2;
return x;
}
int main()
{
int n;
cin >> n;
vector<int> v(n);
for(int i = 0; i < n; i++)
{
cin >> v[i];
}
int k = fun(v[0]);
for(int i = 1; i < n; i++)
{
if(k != fun(v[i]))
{
cout << "NO" << endl;
return 0;
}
}
cout << "YES" << endl;
return 0;
}
66. 装箱问题(动态规划 - 01 背包)
https://ac.nowcoder.com/acm/problem/16693

01背包问题简单应用
cpp
#include <iostream>
using namespace std;
const int N = 35, M = 2e4 + 10;
int n, v;
int arr[N];
int dp[N][M];
int main()
{
cin >> v >> n;
for(int i = 1; i <= n; i++)
{
cin >> arr[i];
}
for(int i = 1; i <= n; i++)
{
for(int j = 0; j <= v; j++)
{
dp[i][j] = dp[i - 1][j];
if(j >= arr[i])
{
dp[i][j] = max(dp[i][j], dp[i - 1][j - arr[i]] + arr[i]);
}
}
}
cout << (v - dp[n][v]) << endl;
return 0;
}
67. 打怪(模拟)
https://ac.nowcoder.com/acm/problem/202487

根据题意模拟
cpp
#include <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
int h, a, H, A;
cin >> h >> a >> H >> A;
if(a >= H)
{
cout << -1 << endl;
}
else
{
int ans = 0;
// 击杀一个怪,你需要承受几次攻击
int round = H/a + (H%a==0?0:1) - 1;
while(h > round*A)
{
h -= round*A;
ans++;
}
cout << ans << endl;
}
}
return 0;
}
68. 字符串的分类(哈希 / 排序)
https://www.nowcoder.com/questionTerminal/9fbb4d95e6164cd9ab52e859fbe8f4ec

将字符串排序后,丢进能去重的哈希表里面即可。
cpp
#include <iostream>
#include <set>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
int n;
cin >> n;
set<string> hash;
for(int i = 0; i < n; i++)
{
string s;
cin >> s;
sort(s.begin(), s.end());
hash.insert(s);
}
cout << hash.size() << endl;
return 0;
}
69. 城市群数量(并查集/搜索)
https://www.nowcoder.com/practice/71cde4dee669475f94d8d38832374ada?tpId=196&tqId=40411&ru=/exam/oj

floodfill问题,这道题可以用bfs/dfs/并查集解决。
搜索:
cpp
class Solution {
public:
bool vis[200];
int n;
int citys(vector<vector<int> >& m)
{
n = m.size();
memset(vis, 0, sizeof vis);
int ret = 0;
for(int i = 0; i < n; i++)
{
if(!vis[i])
{
ret++;
//bfs(m, i);
dfs(m, i);
}
}
return ret;
}
void bfs(vector<vector<int>>& m, int pos)
{
queue<int> q;
q.push(pos);
vis[pos] = true;
while(!q.empty())
{
int i = q.front();
q.pop();
for(int j = 0; j < n; j++)
{
if(m[i][j] == 1 && !vis[j])
{
vis[j] = true;
q.push(j);
}
}
}
}
void dfs(vector<vector<int>>& m, int pos)
{
vis[pos] = true;
for(int j = 0; j < n; j++)
{
if(m[pos][j] == 1 && !vis[j])
{
dfs(m, j);
}
}
}
};
并查集:
cpp
class Solution {
public:
int citys(vector<vector<int> >& m)
{
int n = m.size();
vector<int> v(n, -1);
// 如果v[i]为负,表示这个下标是根,绝对值是节点数量
// 如果v[i]不为负,v[i]是i的父亲下标
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
if(m[i][j] == 1)
{
int root1 = i, root2 = j;
while(v[root1] >= 0)
{
root1 = v[root1];
}
while(v[root2] >= 0)
{
root2 = v[root2];
}
if(root1 != root2)
{
v[root1] += v[root2];
v[root2] = root1;
}
}
}
}
int ans = 0;
for(int i = 0; i < n; i++)
{
if(v[i] < 0)
{
ans++;
}
}
return ans;
}
};
70. 判断是不是平衡二叉树(二叉树 + 递归)
https://www.nowcoder.com/practice/8b3b95850edb4115918ecebdf1b4d222?tpId=13&tqId=11192&ru=/exam/oj

递归计算即可
cpp
/**
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* };
*/
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot)
{
if(pRoot == nullptr)
{
return true;
}
int left = Height(pRoot->left);
int right = Height(pRoot->right);
return IsBalanced_Solution(pRoot->left) && IsBalanced_Solution(pRoot->right) && abs(left-right) <= 1;
}
int Height(TreeNode* root)
{
if(root == nullptr)
{
return 0;
}
return 1 + max(Height(root->left), Height(root->right));
}
};
71. 最大子矩阵(二维前缀和)
https://www.nowcoder.com/practice/a5a0b05f0505406ca837a3a76a5419b3?tpId=230&tqId=40416&ru=/exam/oj

二维前缀和矩阵的应用。
- 初始化二维前缀和矩阵;
- 枚举所有的子矩阵,找出最大子矩阵。
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n;
cin >> n;
vector<vector<int>> nums(n+1, vector<int>(n+1,0));
for(int i = 1; i < n+1; i++)
{
for(int j = 1; j < n+1; j++)
{
cin >> nums[i][j];
}
}
auto dp = nums;
int ans = -128;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + nums[i][j];
ans = max(ans, dp[i][j]);
}
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
for(int ii = i; ii <= n; ii++)
{
for(int jj = j; jj <= n; jj++)
{
ans = max(ans, dp[ii][jj]-dp[i-1][jj]-dp[ii][j-1]+dp[i-1][j-1]);
}
}
}
}
cout << ans << endl;
return 0;
}
72. 小葱的01串(滑动窗口)
https://ac.nowcoder.com/acm/problem/230830

长度固定为原串长度一半的滑动窗口,因为要想符合要求,必定是一半一半的。
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
int n;
cin >> n;
string s;
cin >> s;
int num1 = 0, num0 = 0;
for(char ch : s)
{
if(ch == '1')
num1++;
else
num0++;
}
int ans = 0;
int cur1 = 0, cur0 = 0;
int l = 0, r = 0;
while(cur0 + cur1 < n/2 - 1)
{
if(s[r] == '1')
cur1++;
else
cur0++;
r++;
}
while(r < n)
{
if(s[r] == '1')
cur1++;
else
cur0++;
if(cur1 * 2 == num1 && cur0 * 2 == num0)
ans++;
if(s[l] == '1')
cur1--;
else
cur0--;
r++;
l++;
}
l = n/2 + 1, r = 0;
while(r < n/2 - 1)
{
if(s[r] == '1')
cur1++;
else
cur0++;
if(cur1 * 2 == num1 && cur0 * 2 == num0)
ans++;
if(s[l] == '1')
cur1--;
else
cur0--;
r++;
l++;
}
cout << ans << endl;
return 0;
}