目录
- [73. 笨小猴(模拟)](#73. 笨小猴(模拟))
- [74. 主持人调度(一)(排序)](#74. 主持人调度(一)(排序))
- [75. 分割等和子集(动态规划 - 01背包)](#75. 分割等和子集(动态规划 - 01背包))
- [76. 小红的ABC(字符串 + 找规律)](#76. 小红的ABC(字符串 + 找规律))
- [77. 不相邻取数(动态规划 - 线性 dp)](#77. 不相邻取数(动态规划 - 线性 dp))
- [78. 空调遥控(滑动窗口)](#78. 空调遥控(滑动窗口))
- [79. kotori和气球(组合数学)](#79. kotori和气球(组合数学))
- [80. 走迷宫(BFS)](#80. 走迷宫(BFS))
- [81. 主持人调度(二)(贪心 + 优先级队列)](#81. 主持人调度(二)(贪心 + 优先级队列))
- [82. 游游的重组偶数(数学)](#82. 游游的重组偶数(数学))
- [83. 体操队形(DFS + 枚举)](#83. 体操队形(DFS + 枚举))
- [84. 二叉树中的最大路径和(递归)](#84. 二叉树中的最大路径和(递归))
- [85. 排序子序列(模拟/dp)](#85. 排序子序列(模拟/dp))
- [86. 削减整数(贪心)](#86. 削减整数(贪心))
- [87. 最长上升子序列(二)(贪心 + 二分)](#87. 最长上升子序列(二)(贪心 + 二分))
- [88. 爱吃素(数学)](#88. 爱吃素(数学))
- [89. 相差不超过k的最多数(滑动窗口)](#89. 相差不超过k的最多数(滑动窗口))
- [90. 最长公共子序列(一)(动态规划 - LCS)](#90. 最长公共子序列(一)(动态规划 - LCS))
73. 笨小猴(模拟)
https://www.nowcoder.com/practice/17865bc2a75c4944a872ef709958c56e?tpId=290&tqId=39933&ru=/exam/oj

根据题意模拟
cpp
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
string s;
bool isprim(int n)
{ // 判断 n 是否是质数
if (n < 2) return false;
for (int i = 2; i <= sqrt(n); i++) {
if (n % i == 0) return false;
}
return true;
}
int main()
{
cin >> s;
int hash[26] = { 0 };
for (auto ch : s)
{
hash[ch - 'a']++;
}
int minn = 1000, maxn = 0;
for (int i = 0; i < 26; i++)
{
if (hash[i])
{
minn = min(minn, hash[i]);
maxn = max(maxn, hash[i]);
}
}
if (isprim(maxn - minn))
{
cout << "Lucky Word" << endl;
cout << maxn - minn << endl;
}
else
{
cout << "No Answer" << endl;
cout << 0 << endl;
}
return 0;
}
74. 主持人调度(一)(排序)
https://www.nowcoder.com/practice/e160b104354649b69600803184094adb?tpId=196&tqId=40514&ru=/exam/oj

区间问题技巧:按照左端点排序或者按照右端点排序
左端点排序后,我们仅需考虑后续区间是否能与前一个区间重叠即可。
cpp
class Solution {
public:
bool hostschedule(vector<vector<int> >& schedule)
{
sort(schedule.begin(), schedule.end(), [](vector<int>& v1, vector<int>& v2)->bool{
return v1[0] < v2[0];
});
for (int i = 1; i < schedule.size(); i++)
{
if (schedule[i][0] < schedule[i - 1][1]) return false;
}
return true;
}
};
75. 分割等和子集(动态规划 - 01背包)
https://www.nowcoder.com/practice/65ade309fa4d4067a9add749721bfdc0?tpId=230&tqId=40433&ru=/exam/oj

01 背包问题:原问题转换成,从 n 个数中选,总和恰好为 sum / 2,能否挑选出来。
cpp
#include <iostream>
using namespace std;
const int N = 510, M = 510 * 110 / 2;
int n;
int arr[N];
int dp[N][M];
int main() {
cin >> n;
int sum = 0;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
sum += arr[i];
}
if (sum % 2 == 1) cout << "false" << endl;
else {
sum /= 2;
dp[0][0] = true;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= sum; j++) {
dp[i][j] = dp[i - 1][j];
if (j >= arr[i]) {
dp[i][j] = dp[i][j] || dp[i - 1][j - arr[i]];
}
}
}
if (dp[n][sum]) cout << "true" << endl;
else cout << "false" << endl;
}
return 0;
}
76. 小红的ABC(字符串 + 找规律)
https://ac.nowcoder.com/acm/problem/230720

由于题目要找的是最短的回文子串,并且只有三个字母 a b c,因此最短的回文子串的长度要么是2,要么是 3。
因此,我们仅需枚举所有的二元组以及三元组就好了。
cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string s;
cin >> s;
int ans = -1;
for(int i = 0; i < s.size(); i++)
{
int l = i-1, r = i+1;
if(l >= 0 && r < s.size() && s[l] == s[r]) ans = 3;
l = i, r = i + 1;
if(r < s.size() && s[l] == s[r]) ans = 2;
}
cout << ans << endl;
return 0;
}
77. 不相邻取数(动态规划 - 线性 dp)
https://www.nowcoder.com/practice/a2be806a0e5747a088670f5dc62cfa1e?tpId=230&tqId=39763&ru=/exam/oj

打家劫舍题型
cpp
#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> dp1(n), dp2(n);
// dp1不取 dp2取
dp1[0] = 0;
dp2[0] = nums[0];
for(int i = 1; i < n; i++)
{
dp1[i] = max(dp1[i-1], dp2[i-1]);
dp2[i] = max(dp1[i-1]+nums[i], dp2[i-1]);
}
cout << max(dp1[n-1], dp2[n-1]) << endl;
return 0;
}
78. 空调遥控(滑动窗口)
https://ac.nowcoder.com/acm/problem/229310

滑动窗口:
维护窗口内最大值与最小值的差在 2*p 之间。
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
long long n, p;
cin >> n >> p;
vector<long long> nums(n);
for(int i = 0; i < n; i++)
{
cin >> nums[i];
}
sort(nums.begin(), nums.end());
int ans = 0;
int l = 0, r = 0;
while(r < n)
{
while(nums[r] - nums[l] > 2*p)
{
l++;
}
ans = max(ans, r-l+1);
r++;
}
cout << ans << endl;
return 0;
}
79. kotori和气球(组合数学)
https://ac.nowcoder.com/acm/problem/50039

排列组合问题,第一个位置可以放n种,之后的每个位置都能放n-1种。结果等于n与 m-1个n - 1的乘积。
cpp
#include <iostream>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
int ans = n;
for(int i = 1; i < m; i++)
{
ans = ans * (n-1);
ans %= 109;
}
cout << ans << endl;
return 0;
}
80. 走迷宫(BFS)
https://www.nowcoder.com/practice/e88b41dc6e764b2893bc4221777ffe64?tpId=308&tqId=40477&ru=/exam/oj

经典bfs问题。
cpp
#include <iostream>
#include <string>
#include <queue>
#include <vector>
using namespace std;
int main()
{
int n, m;
int x1, y1, x2, y2;
cin >> n >> m;
cin >> x1 >> y1 >> x2 >> y2;
vector<vector<char>> grid(n, vector<char>(m));
for(int i = 0; i < n; i++)
{
string s;
cin >> s;
for(int j = 0; j < m; j++)
grid[i][j] = s[j];
}
vector<vector<bool>> vis(n, vector<bool>(m, 0));
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
queue<pair<int, int>> q;
q.push({x1-1, y1-1});
vis[x1-1][y1-1] = true;
int ans = 1;
while(!q.empty())
{
int sz = q.size();
while(sz--)
{
auto [a, b] = q.front();
q.pop();
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] == '.' && !vis[x][y])
{
if(x == x2-1 && y == y2-1)
{
cout << ans << endl;
return 0;
}
q.push({x, y});
vis[x][y] = true;
}
}
}
ans++;
}
cout << -1 << endl;
return 0;
}
81. 主持人调度(二)(贪心 + 优先级队列)
https://www.nowcoder.com/practice/4edf6e6d01554870a12f218c94e8a299?tpId=196&tqId=37562&ru=/exam/oj

原数组按照左端点排序。搞个小根堆:
- 先把第一个区间的右端点加入到堆中;
- 遍历后面的区间,分情况讨论:
- 如果左端点大于等于堆顶元素,能接在后面,干掉堆顶,然后把这个区间的右端点加入堆;
- 否则的话,只能再来一个人,只把这个区间的右端点加入堆。
- 最后堆的大小就是需要的人数。
cpp
class Solution
{
public:
int minmumNumberOfHost(int n, vector<vector<int> >& startEnd)
{
sort(startEnd.begin(), startEnd.end(), [](vector<int>& v1, vector<int>& v2){
return v1[0] < v2[0];
});
priority_queue<int, vector<int>, greater<int>> pq;
pq.push(startEnd[0][1]);
for(int i = 1; i < n; i++)
{
int top = pq.top();
if(startEnd[i][0] >= top)
{
pq.pop();
}
pq.push(startEnd[i][1]);
}
return pq.size();
}
};
82. 游游的重组偶数(数学)
https://www.nowcoder.com/questionTerminal/d1ac7f15d5dc40b39a7d6cb11a01407e

从后往前遍历每一位数,把第一个遇到的偶数和个位交换就行了。这种方式也不会出现前导零
cpp
#include <algorithm>
#include <iostream>
using namespace std;
void solve()
{
string s;
cin >> s;
for(int i = s.size()-1; i >= 0; i--)
{
if((s[i]-'0') % 2 == 0)
{
swap(s[s.size()-1], s[i]);
cout << s << endl;
return;
}
}
cout << -1 << endl;
return;
}
int main()
{
int q;
cin >> q;
while(q--)
{
solve();
}
return 0;
}
83. 体操队形(DFS + 枚举)
https://ac.nowcoder.com/acm/problem/229386

dfs,重点是画出决策树和剪枝:从前往后,如果在放置一个人的时候,他的诉求已经被使用,说明这条路径是不合法的。
cpp
#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
int n;
bool vis[11];
int ans;
void dfs(vector<int>& v, int pos)
{
if(pos == n+1)
{
ans++;
return;
}
for(int i = 1; i <= n; i++)
{
if(vis[i] == false)
{
int hope = v[i];
if(vis[hope])
{
return;
}
vis[i] = true;
dfs(v, pos+1);
vis[i] = false;
}
}
}
int main()
{
cin >> n;
vector<int> v(n+1);
memset(vis, 0, sizeof vis);
ans = 0;
for(int i = 1; i <= n; i++)
{
cin >> v[i];
}
dfs(v,1);
cout << ans << endl;
return 0;
}
84. 二叉树中的最大路径和(递归)
https://www.nowcoder.com/practice/da785ea0f64b442488c125b441a4ba4a?tpId=196&tqId=37050&ru=/exam/oj

树形dp:
- 左子树收集:以左子树为起点的最大单链和;
- 右子树收集:以右子树为起点的最大单链和;
- 根节点要做的事情:整合左右子树的信息,得到经过根节点的最大路径和;
- 向上返回:以根节点为起点的最大单链和
cpp
class Solution {
public:
int ret = INT_MIN;
// 找当前子树中的最大值单链
int dfs(TreeNode* root)
{
if(root == nullptr)
return 0;
int l = max(dfs(root->left), 0);
int r = max(dfs(root->right), 0);
ret = max(ret, l + r + root->val);
return max(l, r) + root->val;
}
int maxPathSum(TreeNode* root)
{
dfs(root);
return ret;
}
};
85. 排序子序列(模拟/dp)
https://www.nowcoder.com/questionTerminal/2d3f6ddd82da445d804c95db22dcc471

可以根据题意模拟。也可以用dp。
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int n;
cin >> n;
vector<long long> nums(n);
for(int i = 0; i < n; i++)
{
cin >> nums[i];
}
vector<vector<int>> dp(n, vector<int>(2, 1));
//dp[i][0]升序结束, dp[i][1]降序结束
for(int i = 1; i < n; i++)
{
if(nums[i] > nums[i-1])
{
dp[i][0] = min(dp[i-1][0], dp[i-1][1] + 1);
dp[i][1] = min(dp[i-1][0], dp[i-1][1]) + 1;
}
else if(nums[i] < nums[i-1])
{
dp[i][0] = min(dp[i-1][0], dp[i-1][1]) + 1;
dp[i][1] = min(dp[i-1][1], dp[i-1][0] + 1);
}
else
{
dp[i][0] = dp[i-1][0];
dp[i][1] = dp[i-1][1];
}
}
cout << min(dp[n-1][0], dp[n-1][1]) << endl;
return 0;
}
86. 削减整数(贪心)
https://ac.nowcoder.com/acm/problem/219038

想要最少的次数,应该尽可能将减数翻倍。但是为了确保能恰好减为零,必须保证剩余被减数是翻倍后的减数的倍数。
cpp
#include <iostream>
using namespace std;
int main()
{
int t;
cin >> t;
while(t--)
{
int h;
cin >> h;
int ret = 0;
int x = 1;
while(h)
{
h -= x;
ret++;
if(h % (2*x) == 0)
{
x *= 2;
}
}
cout << ret << endl;
}
return 0;
}
87. 最长上升子序列(二)(贪心 + 二分)
https://www.nowcoder.com/practice/4af96fa010c44638a7e112abf65f7237?tpId=196&tqId=39285&ru=/exam/oj

这是一道非常经典的题型。一般有两种做法,动态规划O(n2),贪心+二分O(nlogn)。这道题的数据量代表要求用后者解决。
cpp
#include <vector>
class Solution {
public:
int LIS(vector<int>& a)
{
int n = a.size();
vector<int> dp(n+1, 0);
// dp[i]为长度为i的最小末尾
int pos = 0;
for(int x : a)
{
if(pos == 0 || x > dp[pos])
{
pos++;
dp[pos] = x;
}
else
{
int l = 1, r = pos;
while(l < r)
{
int m = l + (r-l)/2;
if(dp[m] >= x)
{
r = m;
}
else
{
l = m + 1;
}
}
dp[l] = x;
}
}
return pos;
}
};
88. 爱吃素(数学)
https://ac.nowcoder.com/acm/problem/227310

素数的定义:大于1的自然数,除了1和自身,没有别的因数。
不需要真的相乘ab,分类讨论即可。
cpp
#include <iostream>
#include <cmath>
using namespace std;
bool isprime(long long x)
{
if(x == 1)
return false;
for(long long i = 2; i <= sqrt(x); i++)
{
if(x % i == 0)
{
return false;
}
}
return true;
}
int main()
{
int t;
cin >> t;
while(t--)
{
long long a, b;
cin >> a >> b;
if((a == 1 && isprime(b)) || (b == 1 && isprime(a)))
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}
return 0;
}
89. 相差不超过k的最多数(滑动窗口)
https://www.nowcoder.com/practice/562630ca90ac40ce89443c91060574c6?tpId=308&tqId=40490&ru=/exam/oj

排序后,使用滑动窗口
cpp
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n, k;
cin >> n >> k;
vector<int> nums(n);
for(int i = 0; i < n; i++)
{
cin >> nums[i];
}
sort(nums.begin(), nums.end());
int ans = 0;
int l = 0, r = 0;
while(r < n)
{
while(nums[r] - nums[l] > k)
{
l++;
}
ans = max(ans, r-l+1);
r++;
}
cout << ans << endl;
return 0;
}
90. 最长公共子序列(一)(动态规划 - LCS)
https://www.nowcoder.com/practice/672ab5e541c64e4b9d11f66011059498?tpId=230&tqId=39761&ru=/exam/oj

经典两个字符串之间的 dp 问题
cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
string s1, s2;
int n, m;
cin >> n >> m;
cin >> s1 >> s2;
vector<vector<int>> dp(n, vector<int>(m));
dp[0][0] = s1[0]==s2[0]? 1: 0;
for(int i = 1; i < n; i++)
dp[i][0] = s1[i]==s2[0]? 1: dp[i-1][0];
for(int j = 1; j < m; j++)
dp[0][j] = s1[0]==s2[j]? 1: dp[0][j-1];
for(int i = 1; i < n; i++)
for(int j = 1; j < m; j++)
dp[i][j] = s1[i]==s2[j]? dp[i-1][j-1]+1: max(dp[i-1][j], dp[i][j-1]);
cout << dp[n-1][m-1] << endl;
return 0;
}