🍭 大家好这里是KK爱Coding ,一枚热爱算法的程序员
✨ 本系列打算持续跟新阿里云近期的春秋招笔试题汇总~
💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导
👏 感谢大家的订阅➕ 和 喜欢💗
文章目录
01.K小姐的花园布置
问题描述
K小姐有一个长度为 n n n 的花园,每个位置可以种植一棵红花或者一棵绿植。种植红花可以增加 r i r_i ri 的魅力值,种植绿植可以增加 g i g_i gi 的宁静值。为了避免单调,相邻位置不能种植相同颜色的植物。在满足相邻位置颜色不同的前提下,K小姐想知道如何种植才能使得魅力值和宁静值的总和最大。
输入格式
第一行包含一个正整数 n n n,表示花园的长度。
接下来 n n n 行,每行两个正整数 r i r_i ri 和 g i g_i gi,分别表示在第 i i i 个位置种植红花和绿植时增加的魅力值和宁静值。
输出格式
输出一行一个整数,表示K小姐种植后可以获得的最大总和值。
样例输入
4
1 3
4 3
5 6
2 3
样例输出
15
数据范围
- 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105, 1 ≤ r i , g i ≤ 1 0 9 1 \leq r_i, g_i \leq 10^9 1≤ri,gi≤109。
题解
本题可以使用动态规划来解决。设 d p [ i ] [ 0 ] dp[i][0] dp[i][0] 表示将第 i i i 个位置种植红花,前 i i i 个位置可以获得的最大总和; d p [ i ] [ 1 ] dp[i][1] dp[i][1] 表示将第 i i i 个位置种植绿植,前 i i i 个位置可以获得的最大总和; d p [ i ] [ 2 ] dp[i][2] dp[i][2] 表示前 i i i 个位置可以获得的最大总和,不考虑第 i i i 个位置种植的颜色。
转移方程如下:
- d p [ i ] [ 0 ] = max ( d p [ i − 1 ] [ 1 ] + r i , d p [ i − 1 ] [ 2 ] + r i ) dp[i][0] = \max(dp[i-1][1] + r_i, dp[i-1][2] + r_i) dp[i][0]=max(dp[i−1][1]+ri,dp[i−1][2]+ri)
- d p [ i ] [ 1 ] = max ( d p [ i − 1 ] [ 0 ] + g i , d p [ i − 1 ] [ 2 ] + g i ) dp[i][1] = \max(dp[i-1][0] + g_i, dp[i-1][2] + g_i) dp[i][1]=max(dp[i−1][0]+gi,dp[i−1][2]+gi)
- d p [ i ] [ 2 ] = max ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] ) dp[i][2] = \max(dp[i-1][0], dp[i-1][1], dp[i-1][2]) dp[i][2]=max(dp[i−1][0],dp[i−1][1],dp[i−1][2])
最终答案为 d p [ n − 1 ] [ 2 ] dp[n-1][2] dp[n−1][2]。
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)。
参考代码
- Python
n = int(input())
flowers = [tuple(map(int, input().split())) for _ in range(n)]
dp = [[0] * 3 for _ in range(n)]
dp[0][0] = flowers[0][0]
dp[0][1] = flowers[0][1]
for i in range(1, n):
dp[i][0] = max(dp[i-1][1], dp[i-1][2]) + flowers[i][0]
dp[i][1] = max(dp[i-1][0], dp[i-1][2]) + flowers[i][1]
dp[i][2] = max(dp[i-1])
print(max(dp[n-1]))
- Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] flowers = new int[n][2];
for (int i = 0; i < n; i++) {
flowers[i][0] = sc.nextInt();
flowers[i][1] = sc.nextInt();
}
long[][] dp = new long[n][3];
dp[0][0] = flowers[0][0];
dp[0][1] = flowers[0][1];
for (int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i-1][1], dp[i-1][2]) + flowers[i][0];
dp[i][1] = Math.max(dp[i-1][0], dp[i-1][2]) + flowers[i][1];
dp[i][2] = Math.max(dp[i-1][0], Math.max(dp[i-1][1], dp[i-1][2]));
}
System.out.println(dp[n-1][2]);
}
}
- Cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
vector<vector<int>> flowers(n, vector<int>(2));
for (int i = 0; i < n; i++) {
cin >> flowers[i][0] >> flowers[i][1];
}
vector<vector<long long>> dp(n, vector<long long>(3));
dp[0][0] = flowers[0][0];
dp[0][1] = flowers[0][1];
for (int i = 1; i < n; i++) {
dp[i][0] = max(dp[i-1][1], dp[i-1][2]) + flowers[i][0];
dp[i][1] = max(dp[i-1][0], dp[i-1][2]) + flowers[i][1];
dp[i][2] = max({dp[i-1][0], dp[i-1][1], dp[i-1][2]});
}
cout << dp[n-1][2] << endl;
return 0;
}
02.K小姐的珠宝搭配
问题描述
K小姐是一位知名的珠宝设计师,她最近在设计一款新的珠宝套装。这款套装包含两条长度相等的珠宝链,每条链上都镶嵌了若干颗宝石。为了使套装更加独特,K小姐希望从两条链中分别选出一段等长的区间,使得这两段区间中的宝石种类完全不同。现在给定两条珠宝链的宝石种类信息,请帮助K小姐计算有多少种不同的区间搭配方案。
输入格式
第一行包含一个正整数 n n n,表示每条珠宝链的长度。
第二行包含 n n n 个由空格分开的整数 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,…,an,表示第一条珠宝链上每颗宝石的种类。
第三行包含 n n n 个由空格分开的整数 b 1 , b 2 , … , b n b_1, b_2, \dots, b_n b1,b2,…,bn,表示第二条珠宝链上每颗宝石的种类。
输出格式
输出一个整数,表示不同区间搭配方案的总数。
样例输入
3
1 2 3
3 2 1
样例输出
2
数据范围
对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105, 0 ≤ a i , b i ≤ 1 0 9 0 \leq a_i, b_i \leq 10^9 0≤ai,bi≤109。
题解
本题可以使用双指针或前缀和的思想来解决。我们可以遍历其中一条珠宝链,维护一个变量 c n t cnt cnt 表示当前连续的可以与另一条链搭配的区间长度。当遇到不能搭配的宝石时,就将 c n t cnt cnt 清零并重新开始计数。每次更新 c n t cnt cnt 时,都将其对答案的贡献累加到结果中。
设当前枚举到第 i i i 个位置,如果 a i ≠ b i a_i \neq b_i ai=bi,则 c n t cnt cnt 自增 1 1 1;否则将 c n t cnt cnt 清零。根据容斥原理,以当前位置为右端点的合法区间有 c n t ( c n t + 1 ) 2 \frac{cnt(cnt+1)}{2} 2cnt(cnt+1) 个,将其累加到答案中。
最后,再将最后一段连续可搭配区间的贡献计入答案即可。
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)。
参考代码
- Python
n = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
ans = cnt = 0
for i in range(n):
if a[i] != b[i]:
cnt += 1
else:
ans += cnt * (cnt + 1) // 2
cnt = 0
ans += cnt * (cnt + 1) // 2
print(ans)
- Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n];
int[] b = new int[n];
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();
}
for (int i = 0; i < n; i++) {
b[i] = sc.nextInt();
}
long ans = 0, cnt = 0;
for (int i = 0; i < n; i++) {
if (a[i] != b[i]) {
cnt++;
} else {
ans += cnt * (cnt + 1) / 2;
cnt = 0;
}
}
ans += cnt * (cnt + 1) / 2;
System.out.println(ans);
}
}
- Cpp
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int a[n], b[n];
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
cin >> b[i];
}
long long ans = 0, cnt = 0;
for (int i = 0; i < n; i++) {
if (a[i] != b[i]) {
cnt++;
} else {
ans += cnt * (cnt + 1) / 2;
cnt = 0;
}
}
ans += cnt * (cnt + 1) / 2;
cout << ans << endl;
return 0;
}
03.K小姐的城邦之旅
问题描述
在一片充满奇异风情的大陆上,K小姐被委派完成一项神秘的任务:她要从帝国的中心城邦出发,确保重要的宝物安全送达最边缘的城邦。帝国的城邦沿着一条环形的道路排列,K小姐需要精心筹划在每个城邦的宝物购买与运输计划。
从一个城邦到达相邻城邦会消耗一定量的宝物。每个城邦都有宝物出售,但价格各不相同。K小姐可以根据需要在任何城邦购买任意数量的宝物。不过,如果K小姐携带的宝物超过基本需求量(基本需求量为 1 1 1),将会产生额外的运输成本,即每多携带一单位宝物跨越到下一个城邦,就需要支付一单位的运输费。
K小姐希望以最低的成本安全抵达最后一个城邦。请计算K小姐完成任务所需的最小总花费。
输入格式
第一行包含一个正整数 n n n,代表城邦的个数。
第二行包含 n n n 个由空格分隔的正整数 p 1 , p 2 , … , p n p_1, p_2, \dots, p_n p1,p2,…,pn,代表沿途每个城邦的宝物售价。
输出格式
输出一个整数,表示K小姐抵达最后一个城邦的最小总花费。
样例输入
4
1 3 2 4
样例输出
5
数据范围
对于 100 % 100\% 100% 的评测数据,满足 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105, 1 ≤ p i ≤ 1 0 9 1 \leq p_i \leq 10^9 1≤pi≤109。
题解
本题可以使用动态规划的思想来解决。我们可以定义状态 d p [ i ] dp[i] dp[i] 表示从起点城邦到达第 i i i 个城邦时的最小总花费。那么状态转移方程可以写为:
d p [ i ] = min 0 ≤ j < i { d p [ j ] + p j + ( i − j − 1 ) ⋅ max ( 0 , p j − 1 ) } dp[i] = \min_{0 \leq j < i} \{ dp[j] + p_j + (i - j - 1) \cdot \max(0, p_j - 1) \} dp[i]=0≤j<imin{dp[j]+pj+(i−j−1)⋅max(0,pj−1)}
其中 p j p_j pj 表示在第 j j j 个城邦购买一单位宝物的价格, ( i − j − 1 ) ⋅ max ( 0 , p j − 1 ) (i - j - 1) \cdot \max(0, p_j - 1) (i−j−1)⋅max(0,pj−1) 表示从第 j j j 个城邦运输到第 i i i 个城邦的额外运输费用。
直接使用动态规划求解的时间复杂度为 O ( n 2 ) O(n^2) O(n2),无法在规定时间内完成。我们可以发现,对于每个状态 d p [ i ] dp[i] dp[i],它只与前面的状态有关。因此,我们可以使用单调队列优化动态规划的过程,将时间复杂度降低到 O ( n ) O(n) O(n)。
具体来说,我们维护一个单调递增的队列,队列中存储状态的下标。对于当前状态 d p [ i ] dp[i] dp[i],我们从队首开始,将所有不满足单调性的状态下标从队列中移除,然后将 i i i 加入队列。这样,队首的状态下标就对应了转移到 d p [ i ] dp[i] dp[i] 的最优决策。
参考代码
- Python
from collections import deque
def min_cost(n, prices):
queue = deque([0])
dp = prices[0]
for i in range(1, n - 1):
while queue and prices[queue[0]] + i - queue[0] >= prices[i]:
queue.popleft()
queue.append(i)
dp += prices[queue[0]] + i - queue[0]
return dp
# 读取输入
n = int(input())
prices = list(map(int, input().split()))
# 计算最小总花费
result = min_cost(n, prices)
# 输出结果
print(result)
- Java
import java.util.*;
public class Solution {
public static int minCost(int n, int[] prices) {
Deque<Integer> queue = new ArrayDeque<>();
queue.offerLast(0);
int dp = prices[0];
for (int i = 1; i < n - 1; i++) {
while (!queue.isEmpty() && prices[queue.peekFirst()] + i - queue.peekFirst() >= prices[i]) {
queue.pollFirst();
}
queue.offerLast(i);
dp += prices[queue.peekFirst()] + i - queue.peekFirst();
}
return dp;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取输入
int n = scanner.nextInt();
int[] prices = new int[n];
for (int i = 0; i < n; i++) {
prices[i] = scanner.nextInt();
}
// 计算最小总花费
int result = minCost(n, prices);
// 输出结果
System.out.println(result);
}
}
- Cpp
#include <iostream>
#include <deque>
#include <vector>
using namespace std;
int minCost(int n, vector<int>& prices) {
deque<int> queue;
queue.push_back(0);
int dp = prices[0];
for (int i = 1; i < n - 1; i++) {
while (!queue.empty() && prices[queue.front()] + i - queue.front() >= prices[i]) {
queue.pop_front();
}
queue.push_back(i);
dp += prices[queue.front()] + i - queue.front();
}
return dp;
}
int main() {
// 读取输入
int n;
cin >> n;
vector<int> prices(n);
for (int i = 0; i < n; i++) {
cin >> prices[i];
}
// 计算最小总花费
int result = minCost(n, prices);
// 输出结果
cout << result << endl;
return 0;
}
写在最后
📧 KK这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 KK领取~