🍭 大家好这里是KK爱Coding ,一枚热爱算法的程序员
✨ 本系列打算持续跟新阿里淘天近期的春秋招笔试题汇总~
💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导
👏 感谢大家的订阅➕ 和 喜欢💗
文章目录
01.K小姐的网球俱乐部
问题描述
K小姐是一位网球爱好者,她经营着一家网球俱乐部。俱乐部共有 n n n 名会员,每个会员的网球水平不尽相同,用一个分数 a i a_i ai 来表示。为了激励会员互相切磋球技,俱乐部会定期举办比赛。
每次比赛结束后,参赛人员的分数都会发生变化。比如在第 j j j 场比赛中,获胜者的分数将增加 b j b_j bj 分。K小姐作为俱乐部的会长,她会精心安排每场比赛的对阵名单,让分数最低的会员参加比赛。这样一来,无论输赢,该会员的分数都会得到提升。
K小姐希望通过这种方式,逐步提高整个俱乐部会员的总体水平。
给定俱乐部会员的初始分数和历次比赛的分数变化值,请计算出在每场比赛结束后,俱乐部内最高的会员分数是多少。
输入格式
第一行包含两个正整数 n , m n, m n,m,分别表示会员人数和比赛场次。
第二行共 n n n 个空格分开的正整数 a 1 , a 2 , . . . , a n a_1, a_2, ..., a_n a1,a2,...,an,表示每位会员的初始分数。
第三行共 m m m 个空格分开的正整数 b 1 , b 2 , . . . , b m b_1, b_2, ..., b_m b1,b2,...,bm,表示每场比赛的分数变化值。
输出格式
输出包含 m m m 行,每行一个整数,表示在第 j j j 场比赛结束后,俱乐部内最高的会员分数。
样例输入
5 4
1145 1200 1300 1500 1600
10 270 450 500
样例输出
1600
1600
1650
1800
数据范围
- 对于 30% 的数据,有 1 ≤ n , m ≤ 1000 , 0 ≤ a i , b j ≤ 1 0 9 1 \le n, m \le 1000, 0 \le a_i, b_j \le 10^9 1≤n,m≤1000,0≤ai,bj≤109。
- 对于另外 40% 的数据,有 1 ≤ n , m ≤ 1 0 5 , 0 ≤ a i , b j ≤ 1 0 9 1 \le n, m \le 10^5, 0 \le a_i, b_j \le 10^9 1≤n,m≤105,0≤ai,bj≤109。
- 对于剩余 30% 的数据,无特殊限制。
题解
我们可以维护一个集合,用于存储所有会员的分数。初始时将所有会员的分数插入集合中。每场比赛开始时,我们从集合中取出最小的分数,加上该场比赛的分数变化值,再将新的分数插回集合中。最后,集合中的最大值即为当前最高的会员分数。
该方法的时间复杂度为 O ( ( n + m ) log n ) O((n + m) \log n) O((n+m)logn),空间复杂度为 O ( n ) O(n) O(n)。
参考代码
- Python
from heapq import heappush, heappop
n, m = map(int, input().split())
scores = list(map(int, input().split()))
heap = scores[::]
changes = list(map(int, input().split()))
for change in changes:
min_score = heappop(heap)
new_score = min_score + change
heappush(heap, new_score)
print(max(heap))
- Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(), m = sc.nextInt();
PriorityQueue<Integer> pq = new PriorityQueue<>();
for (int i = 0; i < n; i++) {
pq.offer(sc.nextInt());
}
for (int i = 0; i < m; i++) {
int change = sc.nextInt();
int minScore = pq.poll();
pq.offer(minScore + change);
System.out.println(pq.peek());
}
}
}
- Cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
multiset<int> scores;
for (int i = 0; i < n; i++) {
int score;
cin >> score;
scores.insert(score);
}
while (m--) {
int change;
cin >> change;
auto it = scores.begin();
int minScore = *it;
scores.erase(it);
scores.insert(minScore + change);
cout << *scores.rbegin() << endl;
}
return 0;
}
02.LYA的书架整理
问题描述
LYA是个爱看书的女孩,她有一个长度为 n n n 的书架,上面摆放着各种各样的书。为了让书架看起来更加整洁有条理,LYA决定对书架进行整理。
她的整理方法是:选择一个区间 [ l , r ] [l, r] [l,r] (其中 1 ≤ l ≤ r ≤ n 1 \le l \le r \le n 1≤l≤r≤n),将这个区间内的所有书移走,剩下的部分按原有的顺序重新摆放。如果经过这样的操作后,书架上的书籍从左到右是递增的(或者至少是不递减的),那么LYA就会感到很满意。
现在LYA想知道,有多少种不同的选择区间的方案,能够使得整理后的书架满足她的要求。
给定一个长度为 n n n 的数组 a a a,其中 a i a_i ai 表示第 i i i 本书的编号。请计算出有多少种不同的选择区间的方案,能够使得移走区间内的书后,剩余的书按顺序摆放时是递增的(或至少不递减的)。
输入格式
第一行包含一个正整数 n n n,表示书架的长度。
第二行包含 n n n 个正整数 a 1 , a 2 , . . . , a n a_1, a_2, ..., a_n a1,a2,...,an,表示每本书的编号。
输出格式
输出一个整数,表示合法的选择区间方案数。
样例输入
3
1 2 3
样例输出
6
数据范围
- 1 ≤ n ≤ 2 × 1 0 5 , 1 ≤ a i ≤ 1 0 9 1 \le n \le 2 \times 10^5, 1 \le a_i \le 10^9 1≤n≤2×105,1≤ai≤109。
题解
我们可以从左到右扫描数组,维护一个最小的右端点 min_r \text{min\_r} min_r,表示从左边开始,最右边的连续不递减的位置。初始时 min_r = n \text{min\_r} = n min_r=n。
接下来我们固定左端点 left \text{left} left,从右向左扫描,找到最大的右端点 right \text{right} right,使得区间 [ left , right ] [\text{left}, \text{right}] [left,right] 内的所有数字都大于等于 a left − 1 a_{\text{left} - 1} aleft−1。那么对于固定的 left \text{left} left,所有合法的区间就是 [ left , right ] , [ left , right − 1 ] , . . . , [ left , min_r ] [\text{left}, \text{right}], [\text{left}, \text{right} - 1], ..., [\text{left}, \text{min\_r}] [left,right],[left,right−1],...,[left,min_r],共有 n − right + 1 n - \text{right} + 1 n−right+1 种方案。
最终的答案就是所有 left \text{left} left 对应的方案数之和。时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)。
参考代码
- Python
n = int(input())
a = list(map(int, input().split()))
a = [0] + a + [2 * 10 ** 9]
min_r = n
for i in range(n, 0, -1):
if a[i] <= a[i + 1]:
min_r = i
else:
break
ans = 0
right = min_r - 1
for left in range(1, n + 1):
right = max(right, left)
while right <= n and a[right + 1] < a[left]:
right += 1
ans += n - right + 1
if a[left] < a[left - 1]:
break
print(ans)
- Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] a = new int[n + 2];
a[0] = 0;
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt();
}
a[n + 1] = Integer.MAX_VALUE;
int minR = n;
for (int i = n; i >= 1; i--) {
if (a[i] <= a[i + 1]) {
minR = i;
} else {
break;
}
}
int ans = 0;
int right = minR - 1;
for (int left = 1; left <= n; left++) {
right = Math.max(right, left);
while (right <= n && a[right + 1] < a[left]) {
right++;
}
ans += n - right + 1;
if (a[left] < a[left - 1]) {
break;
}
}
System.out.println(ans);
}
}
- Cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n + 2);
a[0] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
a[n + 1] = INT_MAX;
int minR = n;
for (int i = n; i >= 1; i--) {
if (a[i] <= a[i + 1]) {
minR = i;
} else {
break;
}
}
int ans = 0;
int right = minR - 1;
for (int left = 1; left <= n; left++) {
right = max(right, left);
while (right <= n && a[right + 1] < a[left]) {
right++;
}
ans += n - right + 1;
if (a[left] < a[left - 1]) {
break;
}
}
cout << ans << endl;
return 0;
}
03.K小姐的蛋糕店
问题描述
K小姐是一位蛋糕师,她的蛋糕店最近推出了一款新的蛋糕,这款蛋糕由 n n n 块蛋糕组成,每块蛋糕要么是红丝绒口味(用 R R R 表示),要么是白巧克力口味(用 W W W 表示)。
为了让蛋糕看起来更加美观,K小姐希望通过替换一些蛋糕块的口味,使得最终的蛋糕都是红丝绒口味。每次替换可以选择一段连续的 k k k 块蛋糕,将它们的口味都换成红丝绒。
K小姐最多可以进行 m m m 次替换操作,她想知道 k k k 最小可以是多少,使得在 m m m 次操作内一定能将所有蛋糕块的口味都换成红丝绒。
输入格式
第一行包含两个正整数 n n n 和 m m m,分别表示蛋糕的块数和最多的替换次数。
第二行是一个长度为 n n n 的字符串,每个字符是 R R R 或 W W W,表示初始时每块蛋糕的口味。
输出格式
输出一个整数,表示将所有蛋糕块换成红丝绒口味所需的最小 k k k 值。
样例输入
5 2
WRWWWR
样例输出
3
数据范围
- 1 ≤ n ≤ 2 × 1 0 5 1 \le n \le 2 \times 10^5 1≤n≤2×105
- 1 ≤ m ≤ n 1 \le m \le n 1≤m≤n
题解
题目要求我们找到一个最小的 k k k 值,使得在 m m m 次操作内,可以将所有的白巧克力蛋糕块都替换成红丝绒。
我们可以考虑二分答案,对于一个给定的 k k k 值,判断是否可以在 m m m 次操作内完成替换。判断的方法如下:
- 从左到右扫描字符串,找到第一个白巧克力蛋糕块的位置 p p p。
- 将位置 p p p 到 p + k − 1 p+k-1 p+k−1 的蛋糕块都替换成红丝绒,计数器 c n t cnt cnt 加一。
- 如果 p + k p+k p+k 超过了字符串长度,说明替换完成,跳出循环。
- 否则,从位置 p + k p+k p+k 开始,找到下一个白巧克力蛋糕块的位置 p p p,重复步骤 2。
- 如果计数器 c n t cnt cnt 不超过 m m m,说明当前的 k k k 值可行。
二分的下界为 1,上界为 n n n。如果二分查找的结果是 r r r,说明 r r r 是最小的满足条件的 k k k 值。
时间复杂度为 O ( n log n ) O(n \log n) O(nlogn),空间复杂度为 O ( n ) O(n) O(n)。
参考代码
- Python
def can_replace(k):
cnt = 0
p = 0
while p < n:
if s[p] == 'W':
cnt += 1
p += k
else:
p += 1
return cnt <= m
n, m = map(int, input().split())
s = input()
l, r = 1, n
while l < r:
mid = (l + r) // 2
if can_replace(mid):
r = mid
else:
l = mid + 1
print(l)
- Java
import java.util.*;
public class Main {
static int n, m;
static String s;
public static boolean canReplace(int k) {
int cnt = 0;
int p = 0;
while (p < n) {
if (s.charAt(p) == 'W') {
cnt++;
p += k;
} else {
p++;
}
}
return cnt <= m;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
s = sc.next();
int l = 1, r = n;
while (l < r) {
int mid = (l + r) / 2;
if (canReplace(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
System.out.println(l);
}
}
- Cpp
#include <iostream>
using namespace std;
const int N = 2e5 + 5;
int n, m;
string s;
bool canReplace(int k) {
int cnt = 0;
int p = 0;
while (p < n) {
if (s[p] == 'W') {
cnt++;
p += k;
} else {
p++;
}
}
return cnt <= m;
}
int main() {
cin >> n >> m;
cin >> s;
int l = 1, r = n;
while (l < r) {
int mid = (l + r) / 2;
if (canReplace(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
cout << l << endl;
return 0;
}
写在最后
📧 KK这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 KK领取~