【笔试题汇总】字节跳动2024 春招第一场

这里是paoxiaomo,一个现役ACMer,之后将会持续更新算法笔记系列以及笔试题题解系列
本文章面向想打ICPC/蓝桥杯/天梯赛等程序设计竞赛,以及各个大厂笔试的选手
感谢大家的订阅➕ 和 喜欢💗
有什么想看的算法专题可以私信博主

(本文题面由清隆学长收集)
01.K小姐的礼物选择
问题描述
K小姐在参加一场礼物挑选游戏。游戏中,共有 n n n 个礼物排成一排,每个礼物有两个属性:价值和间隔。第 i i i 个礼物的价值为 v a l u e i value_i valuei,在选取这个礼物后,必须跳过接下来的 s k i p i skip_i skipi 个礼物。K小姐在通过每个礼物时必须决定是否选取,且不能回头选择。K小姐希望你帮助她,计算出她能获得的最大价值总和。
输入格式
第一行包含一个正整数 n n n,表示礼物的数量。
接下来的 n n n 行,每行包含两个正整数 v a l u e i , s k i p i value_i, skip_i valuei,skipi,分别表示第 i i i 个礼物的价值和需要跳过的礼物数量。
输出格式
输出一个整数,表示 K小姐能获得的最大价值总和。
样例输入
3
2 2
1 1
1 1
样例输出
2
评测数据与规模
对于 100 % 100\% 100% 的评测数据, 1 ⩽ n ⩽ 100000 1 \leqslant n \leqslant 100000 1n100000 1 ⩽ v a l u e i , s k i p i ⩽ 100000 1 \leqslant value_i, skip_i \leqslant 100000 1valuei,skipi100000
【试题解析】
这是一个经典的动态规划问题。我们可以使用动态规划来解决。
首先,我们定义一个数组 d p dp dp,其中 d p [ i ] dp[i] dp[i] 表示在考虑前 i i i 个礼物时所能获得的最大总价值。然后,我们可以按顺序考虑每个礼物,更新 d p [ i ] dp[i] dp[i] 的值。
假设我们当前考虑第 i i i 个礼物,那么我们有两种选择:

  1. 选择第 i i i 个礼物,那么我们的总价值将增加 v a l u e i value_i valuei,并且我们必须跳过接下来的 s k i p i skip_i skipi 个礼物。因此,我们可以从 d p [ i − s k i p i ] dp[i-skip_i] dp[iskipi] 中获取前 i − s k i p i i-skip_i iskipi 个礼物的最大总价值,然后加上 v a l u e i value_i valuei,得到选择第 i i i 个礼物时的总价值。
  2. 不选择第 i i i 个礼物,那么我们的总价值将保持不变,继续考虑下一个礼物。
    综合这两种情况,我们可以得到状态转移方程:
    d p [ i ] = max ⁡ ( d p [ i − 1 ] , d p [ i − s k i p i ] + v a l u e i ) dp[i] = \max(dp[i-1], dp[i-skip_i] + value_i) dp[i]=max(dp[i1],dp[iskipi]+valuei)
    最终, d p [ n ] dp[n] dp[n] 即为所求的结果,表示在考虑前 n n n 个礼物时所能获得的最大总价值。
#include <bits/stdc++.h>
using namespace std;

const int maxN = 100010;
using ll = long long;

int n;
vector<vector<ll>> gifts(maxN, vector<ll>(2));

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        int v, s;
        cin >> v >> s;
        gifts[i][0] = v, gifts[i][1] = s;
    }

    vector<ll> dp(n, 0);
    dp[n - 1] = gifts[n - 1][0];

    for (int i = n - 2; i >= 0; i--) {
        dp[i] = dp[i + 1];
        if (i + gifts[i][1] + 1 >= n) {
            dp[i] = max(dp[i], gifts[i][0]);
        } else {
            dp[i] = max(dp[i], gifts[i][0] + dp[i + gifts[i][1] + 1]);
        }
    }
    cout << dp[0] << endl;
    return 0;
}

02.K小姐与A先生的骰子对决
问题描述
K小姐和A先生决定通过掷骰子的方式来一决胜负。他们各自拥有一组特制的骰子,每个骰子的面数不同,分别为 2 2 2 8 8 8 面。掷出每一面的概率都是等可能的。他们将同时掷出所有骰子,并将每个骰子朝上的点数相加。点数总和更高的一方获胜,如果点数相同,则判定为平手。K小姐想知道她获胜的概率是多少,请你帮助她计算。
输入格式
第一行包含两个正整数 n n n m m m,分别代表 K小姐和 A先生各自骰子的数量。
第二行包含 n n n 个正整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an,表示 K小姐每个骰子的面数。
第三行包含 m m m 个正整数 b 1 , b 2 , … , b m b_1, b_2, \ldots, b_m b1,b2,,bm,表示 A先生每个骰子的面数。
输出格式
输出一行,包含一个保留三位小数的浮点数,表示 K小姐获胜的概率。
样例输入
1 3
8
2 3 4
样例输出
0.255
评测数据与规模
对于全部的测试数据,满足 1 ⩽ n , m ⩽ 20 1 \leqslant n,m \leqslant 20 1n,m20 2 ⩽ a i , b i ⩽ 8 2 \leqslant a_i,b_i \leqslant 8 2ai,bi8
【试题解析】
这个问题可以通过暴力搜索或动态规划来解决,但是由于骰子数量可能较大,指数级别复杂度的暴力搜索不太可行,因此我们选择使用动态规划。
首先,我们定义一个数组 d p K dp_K dpK,其中 d p K [ i ] dp_K[i] dpK[i] 表示 K 小姐掷出的点数总和为 i i i 的概率。同样地,定义一个数组 d p A dp_A dpA 表示 A 先生的点数总和为 j j j 的概率。
然后,我们按顺序考虑每个骰子的每个面的点数,更新 d p K dp_K dpK d p A dp_A dpA 的值。具体地,我们可以这样更新:

  1. 对于 K 小姐的每个骰子的每个面的点数,更新 d p K dp_K dpK,计算出掷出点数总和为 i i i 的概率。
  2. 对于 A 先生的每个骰子的每个面的点数,更新 d p A dp_A dpA,计算出掷出点数总和为 j j j 的概率。
    最终,我们可以通过 d p K dp_K dpK d p A dp_A dpA 来计算 K 小姐获胜的概率。具体来说,我们可以遍历所有可能的点数总和 i i i j j j,并统计满足 i > j i > j i>j 的情况出现的概率之和。
    需要注意的是,初始时, d p K [ 0 ] dp_K[0] dpK[0] d p A [ 0 ] dp_A[0] dpA[0] 的值均为 1 1 1,表示掷出点数总和为 0 0 0 的概率为 1 1 1。然后,按照上述方法逐步更新数组 d p K dp_K dpK d p A dp_A dpA 的值。
    最后,将满足条件 i > j i > j i>j 的概率之和作为 K 小姐获胜的概率输出即可。
#include <bits/stdc++.h>
using namespace std;

const int N = 30, M = 200;
int n, m;
int a[N], b[N];
double f[N][M], g[N][M];

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= m; i++) cin >> b[i];
    f[0][0] = g[0][0] = 1.0;

    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= 160; j++) {
            for (int k = j - 1; k >= j - a[i] && k >= 0; k--) {
                f[i][j] += f[i - 1][k] / a[i];
            }
        }
    }

    for (int i = 1; i <= m; i++) {
        for (int j = i; j <= 160; j++) {
            for (int k = j - 1; k >= j - b[i] && k >= 0; k--) {
                g[i][j] += g[i - 1][k] / b[i];
            }
        }
    }

    double res = 0, pre = 0;
    for (int i = 2; i <= 160; i++) {
        pre += g[m][i - 1];
        res += f[n][i] * pre;
    }
    printf("%.3f", res);
    return 0;
}

03.羊群重整计划
问题描述
K 小姐拥有若干羊圈,编号从 − 2 × 1 0 9 -2 \times 10^9 2×109 2 × 1 0 9 2 \times 10^9 2×109。部分羊圈中有羊,而另一些则为空。为了便于管理,K 小姐决定将羊群重新安置,使所有的羊最终都圈在连续编号的 n n n 个羊圈中。每次操作,她可以将一个羊圈中的所有羊转移到任何一个空的羊圈。请问最少需要多少次操作,才能达成目标?
输入格式
第一行包含一个整数 N N N,代表有羊的羊圈数量。
接下来 N N N 行,每行一个整数 a i a_i ai,表示有羊的羊圈编号。
输出格式
输出一个整数,表示最少的转移次数。
样例输入
5
2 0 -1 3 6
样例输出
1
评测数据与规模
对于所有评测数据,有 1 ≤ N ≤ 1 0 5 1 \leq N \leq 10^5 1N105 − 2 × 1 0 9 ≤ a i ≤ 2 × 1 0 9 -2 \times 10^9 \leq a_i \leq 2 \times 10^9 2×109ai2×109。保证所有的 a i a_i ai 均不相同。
【试题解析】
这是一个关于最少操作次数的问题,可以通过贪心算法解决。
首先,我们将有羊的羊圈按编号排序,然后计算相邻羊圈之间的间隔。
接着,我们找到最大的相邻间隔,这个间隔的长度减去1就是最少需要的操作次数。
例如,假设最大的相邻间隔长度为 k k k,那么我们只需将一次羊转移到任何一个空的羊圈,就可以使得这 k k k 个羊圈连续。因此,最少需要的操作次数为 k − 1 k - 1 k1

#include <bits/stdc++.h>

using namespace std;

int main() {
    int N;
    cin >> N;

    vector<int> sheep(N);
    for (int i = 0; i < N; ++i) {
        cin >> sheep[i];
    }

    sort(sheep.begin(), sheep.end());

    int minMoves = 0;
    for (int i = 1; i < N; ++i) {
        minMoves = max(minMoves, sheep[i] - sheep[i - 1] - 1);
    }

    cout << minMoves << endl;

    return 0;
}

整理试题不易,你的关注是我更新的最大动力!关注博主 带你看更多面试及竞赛试题和实用算法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值