字节笔试 字节笔试题 0908
笔试时间:2024年09月08日 秋招
历史笔试传送门:2023秋招笔试合集
第一题
题目
小苯有n个钱包,其中第i个钱包装了ai元,他每天都会恰好使用一个钱包中的钱去购物,尽可能多地购买 一种单价为k元的物品,日复一日,直到所有钱包中的钱都分别买不起此物品。 在小苯在开始日复一日地购物前,可以向任意一些钱包中再加入一些钱,但总共加入的钱数不超过m. 现在小苯想知道,如果自己以最优方案向钱包中加钱,那么最多可以购买多少件此物品。
输入描述
第一行输入三个整数n,k和m(1≤n≤10^5;1≤k≤10^9;0≤m≤10^14)表示小苯的钱包个数,要购买的物品单价,以及小苯最多可以提前加入钱包中的钱。
第二行输入n个正整数a1,a2,...,an(1≤ai≤10^9)表示小苯每个钱包中的钱数。
输出描述
在一行上输出一个整数,表示小苯最多可以购买的此物品数量。
样例输入
5 3 2
4 4 3 1 2
样例输出
4
参考题解
初始购买能力:首先,对于每个钱包 a[i],可以用 a[i] / k 计算出该钱包当前可以购买的商品数量,累加得到总共能购买的商品数。然后,计算每个钱包中还差多少钱可以再购买一个商品,差值为 k - (a[i] % k),如果这个差值是 0 说明已经整除,不需要补钱。补钱优化:将每个钱包补足到下一个能够购买商品的差值存入一个数组 a 中,并对该数组进行排序,优先对差值小的钱包补足。然后,对于每一个差值,判断当前剩余的可补金额 m 是否足够补足。如果足够补足,则购买数量加 1,同时减少 m 的余额。这一过程可以保证在不超过 m 元的限制下,补钱的收益最大化。剩余的钱直接购买:如果 m 还有剩余,直接用剩余的钱再尽可能购买一些商品,也就是计算 m / k,将其加到总购买数量中。
C++:[此代码未进行大量数据的测试,仅供参考]
#include <bits/stdc++.h>
using namespace std;
int main()
{
long long n, m, k;
cin >> n >> k >> m;
vector<long long> a(n);
long long sum = 0;
for (long long i = 0; i < n; i++)
{
long long x;
cin >> x;
sum += x / k;
a[i] = k - x % k;
}
sort(a.begin(), a.end());
for (long long i = 0; i < n; i++)
{
if (m >= a[i])
{
sum++;
m -= a[i];
}
else
{
break;
}
}
sum += m / k;
cout << sum << endl;
}
Java:[此代码未进行大量数据的测试,仅供参考]
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
long n = scanner.nextLong();
long k = scanner.nextLong();
long m = scanner.nextLong();
long[] a = new long[(int) n];
long sum = 0;
for (long i = 0; i < n; i++) {
long x = scanner.nextLong();
sum += x / k;
a[(int) i] = k - x % k;
}
Arrays.sort(a);
for (long i = 0; i < n; i++) {
if (m >= a[(int) i]) {
sum++;
m -= a[(int) i];
} else {
break;
}
}
sum += m / k;
System.out.println(sum);
scanner.close();
}
}
Python:[此代码未进行大量数据的测试,仅供参考]
n, k, m = map(int, input().split())
a = []
sum = 0
for _ in range(n):
x = int(input())
sum += x // k
a.append(k - x % k)
a.sort()
for i in range(n):
if m >= a[i]:
sum += 1
m -= a[i]
else:
break
sum += m // k
print(sum)
第二题
题目
小红有一个长度为2×n-1的数组,每次可以选择其中n个数,将这n个数取反,小红想知道经过若干次操作之后,所有数组元素之和的最大是多少。
输入描述
第一行一个整数n,数组长度为2×n-1
第二行2×n-1个整数,表示数组元素。
1≤n≤10^5; -10^9≤ai≤10^9
输出描述
输出一个整数,表示所有数组元素之和的最大值。
样例输入
3
1 2 3 -4 5
样例输出
15
说明
先选择前三个元素取反,再选择后三个元素取反,数组元素之和最大为15。
参考题解
可以通过贪心算法来解决。目标是通过对数组进行若干次操作,使得数组元素的和尽可能最大化。取绝对值排序:我们可以将数组的所有元素取绝对值,并根据绝对值大小进行排序。因为每次可以将 nnn 个数取反,排序后我们优先将较大的负数取反,使其变为正数,这样能使总和增大。贪心策略:考虑两种情况:如果数组中负数的个数为偶数,说明可以直接将所有负数变为正数;如果负数的个数为奇数,则可以把最小的正数或负数(绝对值最小的那个数)保留为负数,以保证操作次数的限制。
C++:[此代码未进行大量数据的测试,仅供参考]
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n; // 输入 n
int len = 2 * n - 1; // 数组长度为 2 * n - 1
vector<int> arr(len);
// 输入数组元素
for (int i = 0; i < len; i++) {
cin >> arr[i];
}
// 绝对值排序
sort(arr.begin(), arr.end(), [](int a, int b) {
return abs(a) > abs(b);
});
// 贪心:逐个考虑元素
long long sum = 0;
int negative_count = 0;
for (int i = 0; i < len; i++) {
if (arr[i] < 0) {
negative_count++;
}
sum += abs(arr[i]);
}
// 如果负数个数为奇数,则减去最小绝对值的两倍
if (negative_count % 2 == 1) {
sum -= 2 * abs(arr[len - 1]);
}
// 输出结果
cout << sum << endl;
return 0;
}
Java:[此代码未进行大量数据的测试,仅供参考]
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int len = 2 * n - 1;
int[] arr = new int[len];
// 输入数组元素
for (int i = 0; i < len; i++) {
arr[i] = scanner.nextInt();
}
// 绝对值排序
Arrays.sort(arr, (a, b) -> Integer.compare(Math.abs(b), Math.abs(a)));
// 贪心算法:逐个考虑元素
long sum = 0;
int negativeCount = 0;
for (int i = 0; i < len; i++) {
if (arr[i] < 0) {
negativeCount++;
}
sum += Math.abs(arr[i]);
}
// 如果负数个数为奇数,则减去最小绝对值的两倍
if (negativeCount % 2 == 1) {
sum -= 2 * Math.abs(arr[len - 1]);
}
// 输出结果
System.out.println(sum);
scanner.close();
}
}
Python:[此代码未进行大量数据的测试,仅供参考]
n = int(input())
length = 2 * n - 1
arr = list(map(int, input().split()))
# 绝对值排序
arr.sort(key=lambda x: abs(x), reverse=True)
# 贪心算法:逐个考虑元素
sum_value = 0
negative_count = 0
for i in range(length):
if arr[i] < 0:
negative_count += 1
sum_value += abs(arr[i])
# 如果负数个数为奇数,则减去最小绝对值的两倍
if negative_count % 2 == 1:
sum_value -= 2 * abs(arr[length - 1])
# 输出结果
print(sum_value)
第三题
题目
小红有一棵n个点的树,其中1号点是根。每个点有一个权值ai。如果满足任意节点的权值大于等于其子节点的权值和,那么这棵树就是一棵奇妙树。小红每次操作可以选择一个点,将其权值加一。请问小红最少需要多少次操作,才能使这棵树变成一棵奇妙树。
输入描述
第一行一个整数n,表示树的节点数。
第二行n个整数ai,表示每个点的权值。
接下来n-1行,每行两个整数u和v,表示u和v之间有一条边。
2≤n≤10^5
1≤ai≤10^9
1≤u,v≤n
输出描述
输出一个整数,表示最少需要多少次操作。
输出描述 返回一个数字表示结果。
样例输入
3
1 2 3
1 2
1 3
样例输出
4
参考题解
通过DFS来遍历树的每一个节点(树形dfs),并在遍历的过程中计算每个节点的子节点权值和。我们需要最少次数的操作将这棵树变成满足“每个节点的权值大于等于其子节点权值和”的奇妙树。从根节点开始深度优先遍历。对于每个节点,计算其子节点权值的总和 sum,如果子节点权值总和超过当前节点的权值,就记录需要增加的次数。最后输出累积的操作次数。
C++:[此代码未进行大量数据的测试,仅供参考]
#include <bits/stdc++.h>
using namespace std;
vector<int> e[200005];
long long a[200005];
long long ans = 0;
long long dfs(int u, int fa)
{
long long sum = 0;
for (int v : e[u])
{
if (v == fa)
continue;
sum += dfs(v, u);
}
if (sum > a[u])
{
ans += sum - a[u];
}
return max(sum, a[u]);
}
int main()
{
int n;
cin >> n;
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
持续收录字节、腾讯、阿里、美团、美团、拼多多、华为等笔试题解,包含python、C++、Java多种语言版本,持续更新中。
