【阿里淘天笔试题汇总】2024-03-27-阿里淘天春招笔试题-三语言题解(CPP/Python/Java)

🍭 大家好这里是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 1n,m1000,0ai,bj109
  • 对于另外 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 1n,m105,0ai,bj109
  • 对于剩余 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 1lrn),将这个区间内的所有书移走,剩下的部分按原有的顺序重新摆放。如果经过这样的操作后,书架上的书籍从左到右是递增的(或者至少是不递减的),那么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 1n2×105,1ai109

题解

我们可以从左到右扫描数组,维护一个最小的右端点 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} aleft1。那么对于固定的 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,right1],...,[left,min_r],共有 n − right + 1 n - \text{right} + 1 nright+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 1n2×105
  • 1 ≤ m ≤ n 1 \le m \le n 1mn

题解

题目要求我们找到一个最小的 k k k 值,使得在 m m m 次操作内,可以将所有的白巧克力蛋糕块都替换成红丝绒。

我们可以考虑二分答案,对于一个给定的 k k k 值,判断是否可以在 m m m 次操作内完成替换。判断的方法如下:

  1. 从左到右扫描字符串,找到第一个白巧克力蛋糕块的位置 p p p
  2. 将位置 p p p p + k − 1 p+k-1 p+k1 的蛋糕块都替换成红丝绒,计数器 c n t cnt cnt 加一。
  3. 如果 p + k p+k p+k 超过了字符串长度,说明替换完成,跳出循环。
  4. 否则,从位置 p + k p+k p+k 开始,找到下一个白巧克力蛋糕块的位置 p p p,重复步骤 2。
  5. 如果计数器 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领取~

在这里插入图片描述

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值