🍭 大家好这里是KK爱Coding ,一枚热爱算法的程序员
✨ 本系列打算持续跟新饿了么近期的春秋招笔试题汇总~
💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导
👏 感谢大家的订阅➕ 和 喜欢💗
文章目录
01.K小姐的魔法大冒险
问题描述
K小姐正在玩一款魔法冒险游戏。游戏中有 n n n 只怪物,每只怪物有自己的血量上限 h i h_i hi。初始时,所有怪物的血量都等于它们的血量上限。当一只怪物的血量小于等于 0 0 0 时,它就会死亡。
K小姐拥有两个强大的魔法技能:
- 魔法风暴:消耗 1 1 1 点魔法值,对所有存活的怪物造成 1 1 1 点伤害。
- 死亡之触:消耗 2 2 2 点魔法值,可以立即杀死一只已经受伤的怪物(即当前血量小于血量上限的怪物)。
这两个技能都可以使用任意次数。K小姐想知道,最少需要消耗多少魔法值,才能杀死所有怪物,成功通关。
输入格式
第一行包含一个正整数 n n n( 2 ≤ n ≤ 1 0 5 2 \leq n \leq 10^5 2≤n≤105),表示怪物的数量。
第二行包含 n n n 个正整数 h 1 , h 2 , … , h n h_1, h_2, \ldots, h_n h1,h2,…,hn( 1 ≤ h i ≤ 1 0 9 1 \leq h_i \leq 10^9 1≤hi≤109),表示每只怪物的血量上限。
输出格式
输出一个整数,表示K小姐最少需要消耗的魔法值。
样例输入
3
1 1 4
样例输出
3
数据范围
- 2 ≤ n ≤ 1 0 5 2 \leq n \leq 10^5 2≤n≤105
- 1 ≤ h i ≤ 1 0 9 1 \leq h_i \leq 10^9 1≤hi≤109
题解
我们可以先对所有怪物使用魔法风暴,将它们的血量都减少 1 1 1。此时,血量上限为 1 1 1 的怪物就直接死亡了。
接下来,我们可以选择继续使用魔法风暴,或者对已经受伤的怪物使用死亡之触。我们可以计算出,如果我们连续使用魔法风暴,直到最高血量的怪物血量减少到 1 1 1 或以下,一共需要消耗的魔法值。与此同时,我们也可以计算出,如果我们对所有已经受伤的怪物使用死亡之触,一共需要消耗的魔法值。
我们选择消耗魔法值较少的方案,直到所有怪物都被消灭。在样例中,使用 1 1 1 次魔法风暴和 2 2 2 次死亡之触,一共消耗 3 3 3 点魔法值,是最优解。
参考代码
- Python
n = int(input())
h = list(map(int, input().split()))
h.sort() # 将怪物按血量从低到高排序
ans = 1 # 初始消耗1点魔法值用于魔法风暴
zeros = h.count(1) # 统计血量为1的怪物数量
for i in range(zeros, n):
if (n - i) * 2 < h[i] - 1:
# 如果对剩余怪物使用死亡之触的魔法值消耗小于
# 将当前怪物血量降至1所需的魔法风暴次数,
# 则对剩余怪物使用死亡之触
ans += (n - i) * 2
break
else:
# 否则,将当前怪物血量降至1,累计消耗的魔法值
ans += h[i] - 1
print(ans)
- Java
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] h = new int[n];
for (int i = 0; i < n; i++) {
h[i] = sc.nextInt();
}
Arrays.sort(h); // 将怪物按血量从低到高排序
int ans = 1; // 初始消耗1点魔法值用于魔法风暴
int zeros = 0; // 统计血量为1的怪物数量
for (int i = 0; i < n; i++) {
if (h[i] == 1) {
zeros++;
}
}
for (int i = zeros; i < n; i++) {
if ((n - i) * 2 < h[i] - 1) {
// 如果对剩余怪物使用死亡之触的魔法值消耗小于
// 将当前怪物血量降至1所需的魔法风暴次数,
// 则对剩余怪物使用死亡之触
ans += (n - i) * 2;
break;
} else {
// 否则,将当前怪物血量降至1,累计消耗的魔法值
ans += h[i] - 1;
}
}
System.out.println(ans);
}
}
- Cpp
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
int h[n];
for (int i = 0; i < n; i++) {
cin >> h[i];
}
sort(h, h + n); // 将怪物按血量从低到高排序
int ans = 1; // 初始消耗1点魔法值用于魔法风暴
int zeros = 0; // 统计血量为1的怪物数量
for (int i = 0; i < n; i++) {
if (h[i] == 1) {
zeros++;
}
}
for (int i = zeros; i < n; i++) {
if ((n - i) * 2 < h[i] - 1) {
// 如果对剩余怪物使用死亡之触的魔法值消耗小于
// 将当前怪物血量降至1所需的魔法风暴次数,
// 则对剩余怪物使用死亡之触
ans += (n - i) * 2;
break;
} else {
// 否则,将当前怪物血量降至1,累计消耗的魔法值
ans += h[i] - 1;
}
}
cout << ans << endl;
return 0;
}
02.LYA的宝石项链
问题描述
LYA喜欢收集各种漂亮的宝石。她有两条宝石项链,分别记为项链 A A A 和项链 B B B,每条项链都有 n n n 颗宝石。宝石有大有小,每颗宝石都有一个美丽值。
LYA想知道,如果从两条项链中分别选出一些宝石(保持宝石在项链中的相对顺序),使得选出的两组宝石的美丽值之和相等,那么一共有多少种选法。
输入格式
第一行包含一个正整数 n n n( 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105),表示每条项链的宝石数量。
第二行包含 n n n 个正整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an( 1 ≤ a i ≤ 1 0 9 1 \leq a_i \leq 10^9 1≤ai≤109),表示项链 A A A 中每颗宝石的美丽值。
第三行包含 n n n 个正整数 b 1 , b 2 , … , b n b_1, b_2, \ldots, b_n b1,b2,…,bn( 1 ≤ b i ≤ 1 0 9 1 \leq b_i \leq 10^9 1≤bi≤109),表示项链 B B B 中每颗宝石的美丽值。
输出格式
输出一个整数,表示选法的总数。由于答案可能很大,请输出答案对 1 0 9 + 7 10^9 + 7 109+7 取模的结果。
样例输入
3
1 2 3
3 2 1
样例输出
7
数据范围
- 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105
- 1 ≤ a i , b i ≤ 1 0 9 1 \leq a_i, b_i \leq 10^9 1≤ai,bi≤109
题解
本题可以使用哈希表来解决。我们先将两条项链中每颗宝石的美丽值相加,得到一个新的数组。然后我们统计这个新数组中每个美丽值出现的次数。
对于新数组中的每个美丽值,如果它出现了 k k k 次,那么我们可以从这 k k k 个位置中任意选择一些位置,使得选出的宝石的美丽值之和相等。这相当于从 k k k 个元素中选择任意个元素的方案数,即 2 k 2^k 2k。
因此,我们只需要将所有美丽值的方案数相加,再减去空集的方案(即不选任何宝石的方案),就得到了最终的答案。
时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)。
参考代码
- Python
MOD = 10**9 + 7
n = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
cnt = {}
for i in range(n):
s = a[i] + b[i]
cnt[s] = cnt.get(s, 0) + 1
ans = 0
for k in cnt.values():
ans = (ans + pow(2, k, MOD)) % MOD
print((ans - 1 + MOD) % MOD)
- Java
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Main {
static final int MOD = (int) 1e9 + 7;
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();
}
Map<Integer, Integer> cnt = new HashMap<>();
for (int i = 0; i < n; i++) {
int s = a[i] + b[i];
cnt.put(s, cnt.getOrDefault(s, 0) + 1);
}
long ans = 0;
for (int k : cnt.values()) {
ans = (ans + quickPow(2, k)) % MOD;
}
System.out.println((ans - 1 + MOD) % MOD);
}
static long quickPow(long x, int n) {
long res = 1;
while (n > 0) {
if (n % 2 == 1) {
res = res * x % MOD;
}
x = x * x % MOD;
n /= 2;
}
return res;
}
}
- Cpp
#include <iostream>
#include <unordered_map>
using namespace std;
const int MOD = 1e9 + 7;
int quickPow(int x, int n) {
int res = 1;
while (n > 0) {
if (n & 1) {
res = 1LL * res * x % MOD;
}
x = 1LL * x * x % MOD;
n >>= 1;
}
return res;
}
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];
}
unordered_map<int, int> cnt;
for (int i = 0; i < n; i++) {
int s = a[i] + b[i];
cnt[s]++;
}
int ans = 0;
for (auto [_, k] : cnt) {
ans = (ans + quickPow(2, k)) % MOD;
}
cout << (ans - 1 + MOD) % MOD << endl;
return 0;
}
03.K小姐的魔法石分解
问题描述
K小姐是一位魔法师,她拥有许多魔法石。每个魔法石都有一个能量值,能量值等于它的正因子个数。
现在,K小姐想把一些魔法石分解成若干块(也可以不分解),但要满足以下条件:
- 分解后的每块魔法石的能量值必须大于 1 1 1。
- 分解后的所有魔法石的能量值之积等于原魔法石的能量值。
K小姐想知道,对于给定的一块魔法石,如何分解可以使得分解后的所有魔法石的能量值之和最大。
输入格式
第一行包含一个正整数 T T T( 1 ≤ T ≤ 1 0 4 1 \leq T \leq 10^4 1≤T≤104),表示测试数据组数。
接下来 T T T 行,每行包含一个正整数 x x x( 2 ≤ x ≤ 2 × 1 0 5 2 \leq x \leq 2 \times 10^5 2≤x≤2×105),表示原魔法石的能量值。
输出格式
输出共 T T T 行,每行一个整数,表示对应测试数据中,分解后的所有魔法石能量值之和的最大值。
样例输入
3
2
10
123
样例输出
2
4
4
数据范围
- 1 ≤ T ≤ 1 0 4 1 \leq T \leq 10^4 1≤T≤104
- 2 ≤ x ≤ 2 × 1 0 5 2 \leq x \leq 2 \times 10^5 2≤x≤2×105
题解
本题可以使用质因数分解的方法来解决。对于给定的能量值 x x x,我们可以将其分解为若干个质因数的乘积,即 x = p 1 a 1 × p 2 a 2 × … × p k a k x = p_1^{a_1} \times p_2^{a_2} \times \ldots \times p_k^{a_k} x=p1a1×p2a2×…×pkak,其中 p i p_i pi 为质因数, a i a_i ai 为对应的指数。
对于每个质因数 p i p_i pi,它可以被分解成 1 1 1 到 a i a_i ai 块,每块的能量值都是 p i p_i pi。因此,对于原魔法石 x x x,我们可以将其分解成 ( a 1 + 1 ) × ( a 2 + 1 ) × … × ( a k + 1 ) (a_1 + 1) \times (a_2 + 1) \times \ldots \times (a_k + 1) (a1+1)×(a2+1)×…×(ak+1) 块,每块的能量值都是某个质因数的幂次。
最终,分解后的所有魔法石的能量值之和就是 ( a 1 + 1 ) × ( a 2 + 1 ) × … × ( a k + 1 ) (a_1 + 1) \times (a_2 + 1) \times \ldots \times (a_k + 1) (a1+1)×(a2+1)×…×(ak+1)。
特别地,如果原魔法石 x x x 本身就是一个质数,那么最优的分解方案就是不分解,此时能量值之和为 2 2 2。
时间复杂度为 O ( T x ) O(T \sqrt{x}) O(Tx),空间复杂度为 O ( 1 ) O(1) O(1)。
参考代码
- Python
t = int(input())
for _ in range(t):
x = int(input())
factors = []
i = 2
while i * i <= x:
if x % i == 0:
cnt = 0
while x % i == 0:
cnt += 1
x //= i
factors.append((i, cnt))
i += 1
if x > 1:
factors.append((x, 1))
if len(factors) == 1:
print(factors[0][1] * 2)
else:
ans = 1
for _, cnt in factors:
ans *= (cnt + 1)
print(ans)
- Java
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while (t-- > 0) {
int x = sc.nextInt();
List<int[]> factors = new ArrayList<>();
int i = 2;
while (i * i <= x) {
if (x % i == 0) {
int cnt = 0;
while (x % i == 0) {
cnt++;
x /= i;
}
factors.add(new int[]{i, cnt});
}
i++;
}
if (x > 1) {
factors.add(new int[]{x, 1});
}
if (factors.size() == 1) {
System.out.println(factors.get(0)[1] * 2);
} else {
int ans = 1;
for (int[] factor : factors) {
ans *= (factor[1] + 1);
}
System.out.println(ans);
}
}
}
}
- Cpp
#include <iostream>
#include <vector>
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int x;
cin >> x;
vector<pair<int, int>> factors;
int i = 2;
while (i * i <= x) {
if (x % i == 0) {
int cnt = 0;
while (x % i == 0) {
cnt++;
x /= i;
}
factors.emplace_back(i, cnt);
}
i++;
}
if (x > 1) {
factors.emplace_back(x, 1);
}
if (factors.size() == 1) {
cout << factors[0].second * 2 << endl;
} else {
int ans = 1;
for (auto [_, cnt] : factors) {
ans *= (cnt + 1);
}
cout << ans << endl;
}
}
return 0;
}
写在最后
📧 KK这边最近正在收集近一年互联网各厂的笔试题汇总,如果有需要的小伙伴可以关注后私信一下 KK领取~