🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员
✨ 本系列打算持续跟新阿里云近期的春秋招笔试题汇总~
💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导
👏 感谢大家的订阅➕ 和 喜欢💗
📧 清隆这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 清隆领取,会在飞书进行同步的跟新。
文章目录
💌 01.K小姐的红蓝染色方案
问题描述
K小姐拿到了一个由 n n n 个正整数组成的数列 a a a,她准备将其中一些元素染成红色,另外一些元素染成蓝色。K小姐希望染色后红色元素之和与蓝色元素之和相等。
现在,K小姐想知道有多少种不同的染色方案。由于答案可能很大,请输出答案对 1 0 9 + 7 10^9+7 109+7 取模的结果。
输入格式
第一行包含一个正整数 n n n,表示数列的长度。
第二行包含 n n n 个正整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an,表示数列中的元素。
输出格式
输出一个整数,表示不同染色方案的数量对 1 0 9 + 7 10^9+7 109+7 取模的结果。
样例输入
4
1 2 4 3
样例输出
6
数据范围
- 1 ≤ n ≤ 50 1 \leq n \leq 50 1≤n≤50
- 1 ≤ a i ≤ 10 1 \leq a_i \leq 10 1≤ai≤10
题解
本题可以使用记忆化搜索的方法来求解。
定义函数 d f s ( u , l , r ) dfs(u, l, r) dfs(u,l,r) 表示当前考虑到第 u u u 个元素,已经选择的红色元素之和为 l l l,蓝色元素之和为 r r r 时,从第 u u u 个元素开始到最后一个元素的染色方案数量。
对于每个元素,我们有三种选择:
-
不染色,直接考虑下一个元素,此时方案数为 d f s ( u + 1 , l , r ) dfs(u+1, l, r) dfs(u+1,l,r)。
-
染成红色,此时方案数为 d f s ( u + 1 , l + a [ u ] , r ) dfs(u+1, l+a[u], r) dfs(u+1,l+a[u],r)。
-
染成蓝色,此时方案数为 d f s ( u + 1 , l , r + a [ u ] ) dfs(u+1, l, r+a[u]) dfs(u+1,l,r+a[u])。
最终的答案即为 d f s ( 0 , 0 , 0 ) dfs(0, 0, 0) dfs(0,0,0)。
为了避免重复计算,可以使用记忆化搜索,将已经计算过的状态保存下来。同时,s可以在搜索过程中进行剪枝,如果当前已经选择的红色元素之和或蓝色元素之和超过了数列元素和的一半,那么显然无法满足题目要求,可以直接返回 0。
时间复杂度 O ( n × s u m ( a ) ) O(n \times sum(a)) O(n×sum(a)),其中 s u m ( a ) sum(a) sum(a) 表示数列元素之和。空间复杂度 O ( n × s u m ( a ) ) O(n \times sum(a)) O(n×sum(a))。
参考代码
- Python
import sys
sys.setrecursionlimit(10**7)
def main():
n = int(sys.stdin.readline())
a = list(map(int, sys.stdin.readline().split()))
@lru_cache(None)
def dfs(u, l, r):
if u == n:
return int(l == r != 0)
if max(l, r) > sum(a) // 2:
return 0
res = dfs(u+1, l, r)
res += dfs(u+1, l+a[u], r)
res += dfs(u+1, l, r+a[u])
return res % mod
mod = 10**9 + 7
print(dfs(0, 0, 0))
if __name__ == '__main__':
main()
- Java
import java.io.*;
import java.util.*;
public class Main {
static int n;
static int[] a;
static int[][] memo;
static final int MOD = (int)1e9 + 7;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
n = Integer.parseInt(br.readLine());
a = new int[n];
String[] str = br.readLine().split(" ");
for (int i = 0; i < n; i++) {
a[i] = Integer.parseInt(str[i]);
}
int sum = Arrays.stream(a).sum();
memo = new int[n][sum+1];
for (int[] row : memo) {
Arrays.fill(row, -1);
}
System.out.println(dfs(0, 0, 0));
}
static int dfs(int u, int l, int r) {
if (u == n) {
return l == r && l != 0 ? 1 : 0;
}
if (Math.max(l, r) > Arrays.stream(a).sum() / 2) {
return 0;
}
if (memo[u][l] != -1) {
return memo[u][l];
}
int res = dfs(u+1, l, r);
res = (res + dfs(u+1, l+a[u], r)) % MOD;
res = (res + dfs(u+1, l, r+a[u])) % MOD;
return memo[u][l] = res;
}
}
- Cpp
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int MOD = 1e9 + 7;
int n;
vector<int> a;
vector<vector<LL>> memo;
LL dfs(int u, int l, int r) {
if (u == n) {
return l == r && l != 0;
}
if (max(l, r) > accumulate(a.begin(), a.end(), 0) / 2) {
return 0;
}
if (memo[u][l] != -1) {
return memo[u][l];
}
LL res = dfs(u+1, l, r);
res = (res + dfs(u+1, l+a[u], r)) % MOD;
res = (res + dfs(u+1, l, r+a[u])) % MOD;
return memo[u][l] = res;
}
int main() {
cin >> n;
a.resize(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
int sum = accumulate(a.begin(), a.end(), 0);
memo.assign(n, vector<LL>(sum+1, -1));
cout << dfs(0, 0, 0) << endl;
return 0;
}
🎀 02.LYA的魔法操作
问题描述
LYA拥有一个长度为 n n n 的数组 a a a,他可以对数组进行任意次以下三种魔法操作:
- 选择 1 ≤ i < n 1 \leq i < n 1≤i<n,将 a i a_i ai 和 a i + 1 a_{i+1} ai+1 合并为一个数字,结果为 a i & a i + 1 a_i \& a_{i+1} ai&ai+1( & \& & 表示按位与运算)。
- 选择 1 ≤ i < n 1 \leq i < n 1≤i<n,将 a i a_i ai 和 a i + 1 a_{i+1} ai+1 合并为一个数字,结果为 a i ∣ a i + 1 a_i \mid a_{i+1} ai∣ai+1( ∣ \mid ∣ 表示按位或运算)。
- 选择 1 ≤ i < n 1 \leq i < n 1≤i<n,将 a i a_i ai 和 a i + 1 a_{i+1} ai+1 交换位置。
(以上三种操作可以执行任意次,前提是数组长度 n n n 至少为 2 2 2。每次操作执行完后, n n n 都会减少 1 1 1,除了第三种操作)
LYA希望通过魔法操作使得数组的极差最大化,极差的定义为数组最大值与最小值之间的差值。请你帮助LYA求出可以得到的最大极差。
输入格式
第一行包含一个正整数 n n n( 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105),表示数组 a a a 的长度。
第二行包含 n n n 个整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an( 0 ≤ a i ≤ 1 0 9 0 \leq a_i \leq 10^9 0≤ai≤109),表示数组 a a a 的元素。
输出格式
输出一个整数,表示可以得到的最大极差。
样例输入
6
2 2 3 1 1 6
样例输出
7
数据范围
- 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105
- 0 ≤ a i ≤ 1 0 9 0 \leq a_i \leq 10^9 0≤ai≤109
题解
本题可以使用前缀按位与和后缀按位或的思想来解决。
假设我们选择第 i i i 个位置进行划分,那么Left部分应该全部执行按位与操作,Right部分应该全部执行按位或操作。这样Left部分的结果一定小于等于Right部分的结果,才能使得极差最大。
因此,我们可以预处理出每个位置 i i i 右边部分执行按位或操作的结果 f [ i ] f[i] f[i],然后从左到右扫描数组,维护Left部分执行按位与操作的结果 s t st st,并更新答案 r e s = max ( r e s , f [ i + 1 ] − s t ) res = \max(res, f[i+1]-st) res=max(res,f[i+1]−st)。
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)。
参考代码
- Python
n = int(input())
a = list(map(int, input().split()))
f = [0] * (n + 1)
for i in range(n - 1, -1, -1):
f[i] = f[i + 1] | a[i]
st, res = a[0], 0
for i in range(1, n):
st &= a[i]
res = max(res, f[i + 1] - st)
print(res)
- Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[] a = Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray();
int[] f = new int[n + 1];
for (int i = n - 1; i >= 0; i--) {
f[i] = f[i + 1] | a[i];
}
int st = a[0], res = 0;
for (int i = 1; i < n; i++) {
st &= a[i];
res = Math.max(res, f[i + 1] - st);
}
System.out.println(res);
}
}
- Cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<int> f(n + 1);
for (int i = n - 1; i >= 0; i--) {
f[i] = f[i + 1] | a[i];
}
int st = a[0], res = 0;
for (int i = 1; i < n; i++) {
st &= a[i];
res = max(res, f[i + 1] - st);
}
cout << res << endl;
return 0;
}
🍓 03.K小姐的魔法项链
问题描述
K小姐收到了一条由
3
n
3n
3n 颗魔法珠子组成的项链,其中恰好有
n
n
n 颗红色(记为 'r'
)珠子、
n
n
n 颗绿色(记为 'e'
)珠子和
n
n
n 颗蓝色(记为 'd'
)珠子。
现在,K小姐可以进行任意次以下操作:交换项链上任意两颗珠子的位置。请帮助K小姐计算出最少需要多少次操作,可以使得项链上出现
n
n
n 个 "red"
的连续子串。同时,请给出任意一种操作方案。
输入格式
第一行输入一个正整数 n n n。
第二行输入一个长度为
3
n
3n
3n 的、由
n
n
n 个 'r'
、
n
n
n 个 'e'
、
n
n
n 个 'd'
组成的字符串,表示项链的初始状态。
输出格式
第一行输出一个正整数 q q q,表示最少操作次数。
接下来 q q q 行,每行输入两个正整数 x , y x, y x,y,代表交换第 x x x 颗珠子和第 y y y 颗珠子的位置。
样例输入
3
ddrreered
样例输出
4
3 1
8 7
5 2
6 5
数据范围
1 ≤ n ≤ 50000 1 \leq n \leq 50000 1≤n≤50000
题解
本题可以通过贪心的思想来解决。
可以将项链看成一个字符串,目标是通过交换操作,使得字符串中出现
n
n
n 个 "red"
的连续子串。换句话说,我们需要将字符串变成 "redredred..."
的形式。
为了达到这个目标,s可以按照以下步骤进行操作:
- 先将所有的
'r'
移动到位置 0 , 3 , 6 , … , 3 ( n − 1 ) 0, 3, 6, \ldots, 3(n-1) 0,3,6,…,3(n−1) 上。 - 再将所有的
'e'
移动到位置 1 , 4 , 7 , … , 3 ( n − 1 ) + 1 1, 4, 7, \ldots, 3(n-1)+1 1,4,7,…,3(n−1)+1 上。 - 剩下的位置自然就是
'd'
了。
在移动的过程中,如果目标位置上已经是正确的字符,则不需要进行交换操作。
时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)。
参考代码
- Python
n = int(input())
s = input()
mp = {i: c for i, c in enumerate(s)}
r, e, d = set(), set(), set()
for i, c in enumerate(s):
if c == 'r':
r.add(i)
elif c == 'e':
e.add(i)
else:
d.add(i)
res = []
i = 0
for idx in sorted(r):
if idx != i:
res.append([idx, i])
e.discard(i)
e.add(idx) if mp[i] == 'e' else d.add(idx)
mp[idx], mp[i] = mp[i], 'r'
i += 3
i = 1
for idx in sorted(e):
if idx != i:
res.append([idx, i])
d.discard(i)
d.add(idx)
mp[idx], mp[i] = mp[i], 'e'
i += 3
print(len(res))
for x, y in res:
print(x + 1, y + 1)
- Java
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
String s = br.readLine();
char[] mp = s.toCharArray();
List<Integer> r = new ArrayList<>();
List<Integer> e = new ArrayList<>();
List<Integer> d = new ArrayList<>();
for (int i = 0; i < 3 * n; i++) {
if (mp[i] == 'r') {
r.add(i);
} else if (mp[i] == 'e') {
e.add(i);
} else {
d.add(i);
}
}
List<int[]> res = new ArrayList<>();
int i = 0;
for (int idx : r) {
if (idx != i) {
res.add(new int[]{idx, i});
if (mp[i] == 'e') {
e.remove(Integer.valueOf(i));
e.add(idx);
} else {
d.remove(Integer.valueOf(i));
d.add(idx);
}
char temp = mp[idx];
mp[idx] = mp[i];
mp[i] = temp;
}
i += 3;
}
i = 1;
for (int idx : e) {
if (idx != i) {
res.add(new int[]{idx, i});
d.remove(Integer.valueOf(i));
d.add(idx);
char temp = mp[idx];
mp[idx] = mp[i];
mp[i] = temp;
}
i += 3;
}
System.out.println(res.size());
for (int[] p : res) {
System.out.println((p[0] + 1) + " " + (p[1] + 1));
}
}
}
- Cpp
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
string s;
cin >> s;
vector<int> r, e, d;
for (int i = 0; i < 3 * n; i++) {
if (s[i] == 'r') {
r.push_back(i);
} else if (s[i] == 'e') {
e.push_back(i);
} else {
d.push_back(i);
}
}
vector<pair<int, int>> res;
int i = 0;
for (int idx : r) {
if (idx != i) {
res.push_back({idx, i});
if (s[i] == 'e') {
e.erase(find(e.begin(), e.end(), i));
e.push_back(idx);
} else {
d.erase(find(d.begin(), d.end(), i));
d.push_back(idx);
}
swap(s[idx], s[i]);
}
i += 3;
}
i = 1;
for (int idx : e) {
if (idx != i) {
res.push_back({idx, i});
d.erase(find(d.begin(), d.end(), i));
d.push_back(idx);
swap(s[idx], s[i]);
}
i += 3;
}
cout << res.size() << endl;
for (auto p : res) {
cout << p.first + 1 << " " << p.second + 1 << endl;
}
return 0;
}
写在最后
📧 清隆这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 清隆领取,会在飞书进行同步的跟新。