hello 大家好,我是万里
,9.26 我参与了字节跳动的笔试,并成功将其 AK(all-killed)。
众所周知,国庆虽然拥有 7 天的长假(部分好兄弟可能只有 3 天),但真正享受假期的人却寥寥无几,毕竟在今年互联网就业环境并不乐观的情况下,大家只能靠卷来平复自己那颗躁动的心。俗话说得好:国庆一时爽,面试火葬场。为了能斩获大厂的 o f f e r offer offer,请尽快和我一起卷起来吧!
【题目简述】
某家族子孙众多,但保留了部分的家族族谱,现在公司想知道该家族的第一代人是谁以及第 M M M 代人的数量。
【输入描述】
输入的第一行包含一个整数 N < 100 N<100 N<100,表示保留了的族谱数量。
第 2 ∼ N + 1 2\sim N+1 2∼N+1 行每行包含一个字符串数组(数组长度不确定),形如:如: [ A , B , C ] [A,B,C] [A,B,C],其含为 A A A 是 B B B 的父亲, B B B 是 C C C 的父亲。
输入的第一行包含一个整数 M M M,其含义如题意所述。
【输出描述】
输出包含两行,第一行为家族第一代人的姓名,第二行为家族第 M M M 代人的数量。
题目有两个问题:
为了方便后续的处理,我们可以先读入 N N N 组数据,并将 N N N 组数据中涉及到的所有人离散化(字符串转数字,如 A → 1 , B → 2 , C → 3 A\rightarrow 1,B\rightarrow2,C\rightarrow3 A→1,B→2,C→3)。
对于问题1,我们可以维护一个数组: d u [ ] du[] du[],其中 d u [ i ] du[i] du[i] 表示第 i i i 个人的父亲的数量。
若族谱信息为: [ A , B , C ] [A,B,C] [A,B,C],则我们令: d u [ 2 ] + + , d u [ 3 ] + + du[2]++,du[3]++ du[2]++,du[3]++( 2 , 3 2,3 2,3 分别表示 B , C B,C B,C)。那么在处理完所有族谱信息后,我们遍历 d u [ ] du[] du[] 数组,其中 d u [ i ] = 0 du[i]=0 du[i]=0 的 i i i 即为所求。
问题1解决。
对于问题2,我们可以建立“父-子边”(即父亲指向孩子的单向边)。只要题目的输入数据合法(不存在 A A A 是 B B B 的父亲, B B B 又是 A A A 的父亲的情况),那么建完边后所得到的图就必然是棵树。
建立完所有边后,我们从第一代人(祖先)开始 DFS 遍历树,并用 d e p [ ] dep[] dep[] 数组维护树的高度,那么在遍历树的过程中,我们只要统计高度等于 M M M 的节点数量即可得到答案。
【题目简述】
给定一个长度为 n n n 的数组 a 0 , a 1 , a 2 , ⋯ , a n − 1 a_0,a_1,a_2,\cdots,a_{n-1} a0,a1,a2,⋯,an−1,以及一个常数 k k k,请你求出数组 a a a 中有多少个满足以下条件的、长度为 k + 1 k+1 k+1 的子串:
- 2 0 ⋅ a i < 2 1 ⋅ a i + 1 < 2 2 ⋅ a i + 2 < ⋯ < 2 k ⋅ a i + k 2^0\cdot a_i < 2^1 \cdot a_{i+1}<2^2\cdot a_{i+2} < \cdots < 2^k\cdot a_{i+k} 20⋅ai<21⋅ai+1<22⋅ai+2<⋯<2k⋅ai+k
【输入描述】
包含多组测试数据。
第一行给定一个 T T T,表示测试数据的个数。
接下来的 T T T 组测试数据,每组数据首先给定两个整数 n , k n,k n,k,,紧接着输入 n n n 个整数,表示 a 0 , a 2 , ⋯ , a n − 1 a_0,a_2,\cdots,a_{n-1} a0,a2,⋯,an−1。
- 1 ≤ T ≤ 5 1\leq T \leq 5 1≤T≤5。
- 3 ≤ n ≤ 1 0 5 3\leq n \leq 10^5 3≤n≤105
- 0 < k < n 0< k < n 0<k<n
- 0 < a i ≤ 1 0 8 0< a_i \leq 10^8 0<ai≤108
先来思考一个问题,假设现在有一个长度为 x x x 的数组 b [ ] b[] b[],满足:
2 0 ⋅ b 0 < 2 1 ⋅ b 1 < 2 2 ⋅ b 2 < ⋯ < 2 x − 1 b x − 1 2^0\cdot b_0 < 2^1\cdot b_1 < 2^2\cdot b_2 < \cdots < 2^{x-1}b_{x-1} 20⋅b0<21⋅b1<22⋅b2<⋯<2x−1bx−1
那么,其中满足题目限制条件的、长度为 k + 1 k+1 k+1 的子串有多少呢?
我想绝大多数人都能分析的出来:答案是 x − ( k + 1 ) + 1 x - (k+1) + 1 x−(k+1)+1,即 x − k x-k x−k。
简单解释一下:由于整个数组都满足条件,所以其任意长度为 k + 1 k+1 k+1 的子串也必然满足条件。一个长度为 x x x 的数组拥有 x − ( k + 1 ) + 1 x-(k+1)+1 x−(k+1)+1 个长度为 k + 1 k+1 k+1 的子串,固答案为 x − k x-k x−k。
同理,如果数组 a a a 的某个子串长度为 y y y,并且满足题目给出的条件,那么该子串所能为答案带来的贡献就为 y − ( k + 1 ) − 1 y-(k+1)-1 y−(k+1)−1(注意,若 y < k y
于是,我们就可以从头开始遍历数组 a a a,找出其中所有满足题目条件的子串,并根据这些子串的长度,计算它们对答案的贡献。
为了避免贡献被重复计算,我们规定数组的一个元素,只能属于一个子串。
举个例子: a = [ 22 , 12 , 16 , 4 , 3 , 22 , 12 ] a = [22,12,16,4,3,22,12] a=[22,12,16,4,3,22,12],那么满足条件的子串有:
若 k = 2 k=2 k=2,则答案为 ( 3 − 2 ) + ( 4 − 2 ) = 3 (3-2)+(4-2) = 3 (3−2)+(4−2)=3;
若 k = 3 k=3 k=3,则答案为 ( 3 − 3 ) + ( 4 − 3 ) = 1 (3-3) + (4-3) = 1 (3−3)+(4−3)=1。
若 k = 4 k=4 k=4,则答案为 m a x ( 0 , 3 − 4 ) + ( 4 − 4 ) = 0 max(0,3-4) + (4-4) = 0 max(0,3−4)+(4−4)=0。
至此,完成解题。
【题目简述】
你当前位于位置 x x x,你需要走到位置 y y y。
假设每一步对应一个位置。从 x x x 走到 y y y,每一步的长度都是正整数,每一步的值等于上一步的值 -1 或 +0 或 +1,问从 x x x 走到 y y y 最少要走几步。
注意:你走的第一步必须是 1 1 1,最后一步也必须是 1 1 1。
【输入格式】
第一行给定一个整数 T T T,表示有 T T T 组测试数据。
对于每组测试数据,给定两个整数 x , y x,y x,y,其含义如上所述。
【输出格式】
对于每个测试数据,输出一个整数,表示从 x x x 走到 y y y 的最少步数,即答案。
1 ≤ T ≤ 1 0 3 1\leq T\leq 10^3 1≤T≤103
0 ≤ x ≤ y ≤ 2 31 0\leq x \leq y \leq 2^{31} 0≤x≤y≤231。
吐槽:题目明确规定 x ≤ y x\leq y x≤y,但样例中却出现 x > y x > y x>y 的情况,真是让人无 fuck 说。
为了让我们所走的步数尽可能地少,我们得保证所走的每一步的值都要尽可能地大。
不妨假设我们在从 x x x 到 y y y 的过程中,所走的最大值为 n n n。
由于我们所走的第一步、最后一步的值都是 1 1 1,且每一步与上一步的差值不会超过 1 1 1,因此我们所走的路线一定会包含一个(非严格)递增序列以及一个(非严格)递减序列: 1 , 2 , 3 , ⋯ , n , ⋯ , 3 , 2 , 1 1,2,3,\cdots, n,\cdots,3,2,1 1,2,3,⋯,n,⋯,3,2,1。
对于 1 , 2 , 3 , ⋯ , n , ⋯ , 3 , 2 , 1 1,2,3,\cdots, n,\cdots,3,2,1 1,2,3,⋯,n,⋯,3,2,1,我们称其为固定步。
从 x x x 到 y y y 需要走的距离为 y − x y-x y−x,减去固定步: 1 , 2 , 3 , n , max , … , 3 , 2 , 1 1,2,3,n,\max,\dots,3,2,1 1,2,3,n,max,…,3,2,1 后,剩余需要走的距离为:
y − x − 1 , 2 , 3 , ⋯ , n , … , 3 , 2 , 1 = y − x − ( 2 × n ( n + 1 ) 2 − n ) = y − x − n × ( n + 1 ) + n \begin{aligned} y-x-1,2,3,\cdots,n,\dots,3,2,1&=y-x-(2\times \dfrac{n(n+1)}{2}-n)\\ &=y-x-n\times(n+1)+n \end{aligned} y−x−1,2,3,⋯,n,…,3,2,1=y−x−(2×2n(n+1)−n)=y−x−n×(n+1)+n
为了方便阐述,我们可以定义 c h a cha cha 表示剩余需要走的距离,起初 c h a = y − x − n × ( n + 1 ) + n cha = y-x-n\times(n+1)+n cha=y−x−n×(n+1)+n。
我们来对 c h a cha cha 进行一波分类讨论:
当 c h a = 0 cha = 0 cha=0 时:已到达位置 y y y,总共走的步数为 2 × n − 1 2\times n - 1 2×n−1(固定步数)。
当 0 < c h a ≤ n 0< cha \leq n 0<cha≤n 时:距离位置 y y y 仅有一步之遥,我们可以在固定步的基础上多走一步到达 y y y,即:
固定步:
1 , 2 , 3 , ⋯ , c h a , c h a + 1 , ⋯ , n , ⋯ , c h a + 1 , c h a , ⋯ , 3 , 2 , 1 1,2,3,\cdots,cha,cha+1,\cdots,n,\cdots,cha+1,cha,\cdots,3,2,1 1,2,3,⋯,cha,cha+1,⋯,n,⋯,cha+1,cha,⋯,3,2,1
多走一步:
1 , 2 , 3 , ⋯ , c h a , c h a , c h a + 1 , ⋯ , n , ⋯ , c h a + 1 , c h a , ⋯ , 3 , 2 , 1 1,2,3,\cdots,cha,cha,cha+1,\cdots,n,\cdots,cha+1,cha,\cdots,3,2,1 1,2,3,⋯,cha,cha,cha+1,⋯,n,⋯,cha+1,cha,⋯,3,2,1
或:
1 , 2 , 3 , ⋯ , c h a , c h a + 1 , ⋯ , n , ⋯ , c h a + 1 , c h a , c h a , ⋯ , 3 , 2 , 1 1,2,3,\cdots,cha,cha+1,\cdots,n,\cdots,cha+1,cha,cha,\cdots,3,2,1 1,2,3,⋯,cha,cha+1,⋯,n,⋯,cha+1,cha,cha,⋯,3,2,1
总共走的步数为 ( 2 × n − 1 ) + 1 = 2 × n (2\times n - 1) + 1 = 2\times n (2×n−1)+1=2×n。
当 c h a > n cha > n cha>n 时:此时我们并没法通过一步来到达 y y y。
为了使我们走的步数尽可能少,我们要保证每步走的值尽可能大。于是,我们可以轻易地制定贪心策略:
若 c h a > n cha > n cha>n ,则我们每一步都走 n n n,直到 c h a ≤ n cha \leq n cha≤n(变为情况 1 1 1 或情况 2 2 2)。
该策略总共要走的步数为 2 × n − 1 + ⌈ c h a n ⌉ 2\times n - 1 + \lceil \dfrac{cha}{n} \rceil 2×n−1+⌈ncha⌉, ⌈ ⌉ \lceil \rceil ⌈⌉ 表示向上取整。
在经过上述讨论后,不难发现,我们只要能取到一个最大值 n n n,就能快速计算出需要走的步数。
对于最大值 n n n,我们可以采用枚举法来确定,即逐一判断最大值取不同数时对应的不同步数,再从中取最优解作为答案。
不过既然采用枚举法,就要确定枚举的上限:根据 x , y ≤ 2 31 − 1 x,y\leq 2^{31}-1 x,y≤231−1 可得 x , y x,y x,y 之间的距离最大为 2 31 2^{31} 231 左右。
最大值 n n n 的固定步数对应的距离为 1 + 2 + 3 + ⋯ + n + ⋯ + 3 + 2 + 1 = n ( n + 1 ) − n 1+2+3+\cdots + n +\cdots + 3 + 2 + 1 = n(n+1) - n 1+2+3+⋯+n+⋯+3+2+1=n(n+1)−n。由于我们走的每一步都必须为正数(不能往回走),所以需要保证:
n ( n + 1 ) − n < 2 31 n(n+1) - n < 2^{31} n(n+1)−n<231
可得 n n n 的最大取值为 5 × 1 0 4 5\times 10^4 5×104 左右。
总时间复杂度为 O ( T × n ) = O ( 1000 × 5 × 1 0 4 ) = O ( 5 × 1 0 7 ) O(T\times n) = O(1000\times 5\times 10^4) = O(5\times 10^7) O(T×n)=O(1000×5×104)=O(5×107) 。
【题目简述】
小明买了只狗以及一包 M M M 克的狗粮。
小狗每天可能会吃 a 1 a_1 a1、 a 2 a_2 a2、 ⋯ \cdots ⋯ 狗粮。
只要狗粮的数量不为 0 0 0,那么即使当天狗粮不够小狗吃,小狗那天也算吃了一天的狗粮。
现已知小狗后一天吃的狗粮不会比前一天多,问它吃完这包狗粮一共会有多少种情况?
【输入格式】
输入共两行。
第一行:空格分割小狗每天的可能的食量 a 1 , a 2 , ⋯ a_1,a_2,\cdots a1,a2,⋯,最多 10 10 10 个。
第二行:输入 M M M,表示狗粮一共有多少克。
【输出格式】
输出一个整数,表示这袋狗粮小狗吃完的情况数。
1 ≤ a i , M ≤ 1 0 4 1\leq a_i,M \leq 10^4 1≤ai,M≤104。
既然要求的是情况数,那么不妨考虑动态规划。
根据题目已有信息,我们可以定义 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示剩余狗粮为 i i i,上一天吃的狗粮为 j j j 的方案(情况)数。
整个 d p dp dp 的转移我们可以使用三个 f o r for for 循环来实现:
根据由题意可得,当 a [ k ] ≥ a [ i ] a[k] \geq a[i] a[k]≥a[i] 时:
d p [ i ] [ j ] + = d p [ k ] [ j + a [ k ] ] dp[i][j] += dp[k][j + a[k]] dp[i][j]+=dp[k][j+a[k]]
总时间复杂度为 O ( 10 × 1 0 4 × 1 0 4 ) = O ( 1 0 9 ) O(10\times 10^4 \times 10^4) = O(10^9) O(10×104×104)=O(109)。
由于本题存在约束条件,即小狗当天吃的狗粮量不会大于上一天狗粮量,所以我们可以进行相应剪枝,降低常数来通过本题。
当下互联网各大厂越来越注重算法,算法也往往会被用来衡量你的代码能力。作为想要顺利拿到大厂 o f f e r offer offer 的你,算法是必不可缺的一项技能。如果你认为自己的算法水平薄弱,或是需要有人督促学习,那么请添加我的 QQ:670369632
,我会每日挑选一道题经典算法题,与你分享交流,并督促你刷题。