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

🍭 大家好这里是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 1n105 1 ≤ r i , g i ≤ 1 0 9 1 \leq r_i, g_i \leq 10^9 1ri,gi109

题解

本题可以使用动态规划来解决。设 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[i1][1]+ri,dp[i1][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[i1][0]+gi,dp[i1][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[i1][0],dp[i1][1],dp[i1][2])

最终答案为 d p [ n − 1 ] [ 2 ] dp[n-1][2] dp[n1][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 1n105 0 ≤ a i , b i ≤ 1 0 9 0 \leq a_i, b_i \leq 10^9 0ai,bi109

题解

本题可以使用双指针或前缀和的思想来解决。我们可以遍历其中一条珠宝链,维护一个变量 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 1n105 1 ≤ p i ≤ 1 0 9 1 \leq p_i \leq 10^9 1pi109

题解

本题可以使用动态规划的思想来解决。我们可以定义状态 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]=0j<imin{dp[j]+pj+(ij1)max(0,pj1)}

其中 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) (ij1)max(0,pj1) 表示从第 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领取~

在这里插入图片描述

  • 28
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值