Author:baiyucraft
BLog: baiyucraft’s Home
0304 美团 春招 第一题:平滑值 题目 小红定义一个数组的平滑值为:任意两个相邻元素的差的绝对值的最大值。例如:数组 [2,4,3,3]
的平滑值为 |2-4|=2
。
小红希望你构造一个长度为 n
的排列,满足排列的平滑值等于 k
。你能帮帮她吗?排列是指:长度为n的数组,1到n每个正整数都恰好出现 1
次。
输入描述
第一行输入两个正整数,分别表示n和k。其中,1 ≤ k < n ≤ 1 0 5 1 \le k \lt n \le 10^5 1 ≤ k < n ≤ 1 0 5
输出描述
输出 n
个数字,用空格隔开。输入 4 2
输出 1 3 2 4
代码 脑筋急转弯
1 2 3 4 5 6 7 8 const { readLineNum } = require ('../utils/read' );const [n, k] = readLineNum ();const arr1 = Array .from ({ length : k }, (_, i ) => i + 1 );const arr2 = Array .from ({ length : n - k - 1 }, (_, i ) => i + k + 2 );const arr = [k + 1 , ...arr1, ...arr2];console .log (arr.join (' ' ));
第二题:p的倍数 题目 小红拿到了一个正整数 n
,她可以进行若干次操作,每次操作将选择一个数位,使其加 1
或者减 1
。
不过有两条限制:
每个数位最多只能操作一次。
如果选择的是 9
,则无法进行加 1
操作。如果选择的是 0
,则无法进行减 1
操作。
小红希望最终 n
成为 p
的倍数,你能帮小红输出操作结束后的整数 n
吗?
输入描述
两个正整数 n
和 p
。1 ≤ n , p ≤ 1 0 13 1 \le n, p \le 10^{13} 1 ≤ n , p ≤ 1 0 13
输出描述
如果误解,请输出 -1
。假设有多解的时候,输出任意解即可。(如果操作包含前导零,请将前导零一起输出)
代码 枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 const { readLineNum } = require ('../utils/read' );const [n, p] = readLineNum ();const arr = [];let tmpN = n;while (tmpN > 0 ) { arr.push (tmpN % 10 ); tmpN = Math .floor (tmpN / 10 ); } arr.reverse ();let res = -1 ;const dfs = (cur, digits ) => { if (res !== -1 || digits === arr.length ) return ; if (cur % p === 0 ) { res = cur; return ; } dfs (cur, digits + 1 ); if (arr[digits] !== 0 ) { dfs (cur - 10 ** digits, digits + 1 ); } if (arr[digits] !== 9 ) { dfs (cur + 10 ** digits, digits + 1 ); } };dfs (n, 0 );console .log (res);
0307 携程 春招 第一题:游游的字符串 题目 游游拿到一个长度为 n
的字符串,她每次操作会选择一个区间 [l, r]
,将第 l
个字母到第 r
个字母各重复一次,插入到该字母的后面。
例如,对于字符串 “abcd”
,若选择区间 [2, 3]
进行操作,字符串将变成 “abbccd”
。
游游将进行 q
次操作。她想知道,q
次操作结束后,最终字符串是什么样子?
输入描述
第一行输入两个正整数 n
和 q
,分别代表字符串的长度和操作次数。
第二行输入一个仅有小写英文字母组成的字符串,代表初始的字符串。
接下来 q
行,每行输入两个正整数 l, r
,代表操作的区间。
限制:1 ≤ n ≤ 1000 1 \le n \le 1000 1 ≤ n ≤ 1000 ,1 ≤ q ≤ 10 1 \le q \le 10 1 ≤ q ≤ 10 ,1 ≤ l ≤ r ≤ 1 0 6 1 \le l \le r \le 10^6 1 ≤ l ≤ r ≤ 1 0 6 ,保证每次操作时,r
不大于当前的字符串长度。
示例
1 2 3 4 5 6 7 6 2 abcdef2 4 3 6 abbbccccdddef
代码 1 2 3 4 5 6 7 8 9 10 11 12 const { readlines } = require ('../utils/read' );const data = readlines ();const [, str, ...queries] = data;const arr = Array .from (str);while (queries.length ) { const [l, r] = queries.shift (); const mid = arr.slice (l - 1 , r).reduce ((pre, val ) => [...pre, val, val], []); arr.splice (l - 1 , r - l + 1 , ...mid); }console .log (arr.join ('' ));
第二题:游游出游 题目 游游准备开车出游,她的车非常特殊,油越多则最高速度越快,即最高速度和油量是成正比的。另外,行驶过程中油是不会消耗的。
已知游游的车初始的最高速度为 v0
,当游游花费了 t
时间加油时,车的最高速度会变成 v0 + t * x
。
游游开车的总里程为 y
,假设游游始终以最高速度行驶(即忽略加速时间),游游想知道,自己最少花费多少时间可以完成出游?
输入描述
三个整数 v0
, x
, y
, 用空格隔开。0 ≤ v 0 ≤ 1 0 9 0 \le v_0 \le 10^9 0 ≤ v 0 ≤ 1 0 9 , 1 ≤ x , y ≤ 1 0 9 1 \le x,y \le 10^9 1 ≤ x , y ≤ 1 0 9
输出描述
一个浮点数,代表最终花费的总时间。如果你的答案和标准答案的相对误差不超过 1 0 − 6 10^-6 1 0 − 6 ,则认为答案正确。
示例
代码 数学做法,设加油时间为 t t t ,则总时间为f ( t ) = y v o + x t + t f(t) = \dfrac{y}{v_o + xt} + t f ( t ) = v o + x t y + t ,此时对 f ( t ) f(t) f ( t ) 求导,可得 f ′ ( t ) = − x y ( v o + x t ) 2 + 1 f'(t) = -\dfrac{xy}{(v_o + xt)^2} + 1 f ′ ( t ) = − ( v o + x t ) 2 x y + 1 ,此时令 f ′ ( t ) = 0 f'(t) = 0 f ′ ( t ) = 0 ,可得 x y = ( v o + x t ) 2 xy = (v_o + xt)^2 x y = ( v o + x t ) 2 ,即 x y − v o x \dfrac{\sqrt{xy} - v_o}{x} x x y − v o 。
1 2 3 4 5 6 const { readLineNum } = require ('../utils/read' );const [v0, x, y] = readLineNum ();const t = (Math .sqrt (x * y) - v0) / x;const ft = y / (v0 + x * t) + t;console .log (ft);
第三题:游游购物 题目 游游正在逛超市,有 n
个商品摆成一排,第 i
个商品的价格为 ai
,,游游对它的喜爱度为 bi
。所有商品的价格都是偶数。
超市开展了一个活动,当游游花费原价买了一件商品时,她可以用半价买下一件右边相邻的商品(也可以用原价购买,这样该商品右边的商品就有一次享受半价的机会)。但如果游游半价购买了一件商品,那么下一件右边相邻的商品只能原价购买。
换言之,如果游游想要半价买某一件商品,必须先用原价买下它相邻的左边的那个商品。
游游初初始的钱为 x
,她想要买的商品的喜爱度总和尽可能大,但总价格不能超过 x
。你能帮帮她计算最大的喜爱度总和吗?
输入描述
第一行输入两个正整数 n
和 x
,分别代表商品的数量,以及游游初始的金额数。
第二行输入 n
个正整数 ai
,分别代表每个商品的价格。
第三行输入 n
个正整数 bi
,分别代表每个商品可以给游游带来的喜爱度。
1 ≤ n , x , a i ≤ 1000 1 \le n, x, a_i \le 1000 1 ≤ n , x , a i ≤ 1000 , 1 ≤ b i ≤ 1 0 9 1 \le b_i \le 10^9 1 ≤ b i ≤ 1 0 9 , 保证所有的ai都是偶数。
输出描述
一个整数,代表最终喜爱度之和的最大值。
示例
1 2 3 4 5 6 4 7 2 2 6 2 3 4 5 1 12
代码 递归
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const { readlinesNum } = require ('../utils/read' );const [[n, x], a, b] = readlinesNum ();const dfs = (index, money, half ) => { if (index === n) return 0 ; if (money < a[index] / 2 ) return dfs (index + 1 , money, false ); if (!half && money < a[index]) return dfs (index + 1 , money, false ); return Math .max ( half ? dfs (index + 1 , money - a[index] / 2 , false ) + b[index] : 0 , dfs (index + 1 , money - a[index], true ) + b[index], dfs (index + 1 , money, false ) ); };console .log (dfs (0 , x, false ));
记忆化搜索优化,空间换时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const memo = Array .from ({ length : n }, () => Array .from ({ length : x + 1 }, () => Array (2 ).fill (-1 )));const dfs = (index, money, half ) => { if (index === n || money < 0 ) return 0 ; if (memo[index][money][half] !== -1 ) return memo[index][money][half]; let ans = 0 ; if (money < a[index] / 2 ) { ans = dfs (index + 1 , money, 0 ); } else if (!half && money < a[index]) { ans = dfs (index + 1 , money, 0 ); } else { ans = Math .max ( half ? dfs (index + 1 , money - a[index] / 2 , 0 ) + b[index] : 0 , dfs (index + 1 , money - a[index], 1 ) + b[index], dfs (index + 1 , money, 0 ) ); } memo[index][money][half] = ans; return ans; };dfs (0 , x, 0 );console .log (memo[0 ][x][0 ]);
0311 美团 春招实习 第一题:小美的字符串 题目 小美有一个由数字字符组成的字符串。现在她想对这个字符串进行一些修改。具体地,她可以将这个字符串中任意位置字符修改为任意的数字字符。她想知道,至少进行多少次修改,可以使修改后的字符串不包含两个连续相同的字符?
例如,对于字符串 ”111222333”
,她可以进行 3
次修改将其变为 ”121212313"
。
输入描述
一行, 一个字符串 s
,保证 s
只包含数字字符。
1 ≤ ∣ s ∣ ≤ 100000 1 \le |s| \le 100000 1 ≤ ∣ s ∣ ≤ 100000
输出描述
一行,一个整数,表示修改的最少次数。
示例
1 2 3 111222333 -> 3 11551111 -> 4
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const { readline } = require ('../utils/read' );let s = readline ();if (s.length < 2 ) console .log (0 ); s += 'e' ;let res = 0 ;let i = 1 ; let cur = s[0 ]; let count = 1 ;while (i < s.length ) { if (s[i] !== cur) { res += count >> 1 ; cur = s[i]; count = 1 ; } else { ++count; } ++i; }console .log (res);
第二题:流星 题目 小美是一位天文爱好者,她收集了接下来一段时间中所有会划过她所在的观测地上空的流星信息。具体地,她收集了 n
个流星在她所在观测地上空的出现时刻和消失时刻。对于一个流星,若其的出现时刻为 s
,消失时刻为 t
, 那么小美在时间段 [s, t]
都能够观测到它。对于一个时刻,观测地上空出现的流星数量越多,则小美认为该时刻越好。小美希望能够选择一个最佳的时刻进行观测和摄影,使她能观测到最多数量的流星。现在小美想知道,在这个最佳时刻,她最多能观测到多少个流星以及一共有多少个最佳时刻可供她选择。
输入描述
第一行是一个正整数 n
,表示流星的数量。
第二行是 n
个用空格隔开的正整数,第 i
个数 si
表示第 i
个流星的出现时间。
第三行是 n
个用空格隔开的正整数,第 i
个数 ti
表示第 i
个流星的消失时间。
1 ≤ n ≤ 100000 1 \le n \le 100000 1 ≤ n ≤ 100000 , 1 ≤ s i ≤ t i ≤ 1 0 9 1 \le si \le ti \le 10^9 1 ≤ s i ≤ t i ≤ 1 0 9
输出描述
输出一行用空格隔开的两个数x和y,其中x表示小美能观测到的最多的流行数,y表示可供她选择的最佳时刻数量。
示例
代码 遍历时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const { readlinesNum } = require ('../utils/read' );const [n, s, t] = readlinesNum ();const points = [ ...s.map (val => [val, 1 ]), ...t.map (val => [val + 1 , -1 ]) ]; points.sort ((a, b ) => a[0 ] - b[0 ]);let i = 0 ; let maxMeteors = 0 ; let currentMeteors = 0 ; let bestTimeCount = 0 ;for (let time = points[0 ][0 ]; time <= points[points.length - 1 ][0 ]; ++time) { while (i < points.length && time === points[i][0 ]) { currentMeteors += points[i][1 ]; ++i; } if (currentMeteors === maxMeteors) { ++bestTimeCount; } else if (currentMeteors > maxMeteors) { maxMeteors = currentMeteors; bestTimeCount = 1 ; } }console .log (maxMeteors, bestTimeCount);
线段树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 const { readlinesNum } = require ('../utils/read' );const [n, s, t] = readlinesNum ();class SegmentTreeNode { constructor (start, end ) { this .start = start; this .end = end; this .left = null ; this .right = null ; this .val = { count : 0 , num : 0 }; } }class SegmentTree { constructor (appearanceTimes, disappearanceTimes ) { this .start = Math .min (...appearanceTimes); this .end = Math .max (...disappearanceTimes); this .root = new SegmentTreeNode (this .start , this .end ); for (let i = 0 ; i < appearanceTimes.length ; ++i) { this .updateNode (this .root , appearanceTimes[i], disappearanceTimes[i]); } } updateNode (node, start, end ) { if (node.start === node.end ) { node.val .count += 1 ; node.val .num = 1 ; return ; } const mid = (node.start + node.end ) >> 1 ; if (node.start <= end && start <= mid) { if (!node.left ) node.left = new SegmentTreeNode (node.start , mid); this .updateNode (node.left , start, end); } if (mid + 1 <= end && start <= node.end ) { if (!node.right ) node.right = new SegmentTreeNode (mid + 1 , node.end ); this .updateNode (node.right , start, end); } } _query (node ) { if (!node.left && !node.right ) return node.val ; const leftVal = this ._query (node.left ); const rightVal = this ._query (node.right ); node.val .count = Math .max (leftVal.count , rightVal.count ); if (leftVal.count === node.val .count ) { node.val .num += leftVal.num ; } if (rightVal.count === node.val .count ) { node.val .num += rightVal.num ; } return node.val ; } query ( ) { return this ._query (this .root ); } }const segmentTree = new SegmentTree (s, t); segmentTree.query ();console .log (Object .values (segmentTree.root .val ).join (' ' ));
第三题:最佳规划 题目 小团在一个 n*m
的网格地图上探索。网格地图上第 i
行第 j
列的格子用坐标 (i,j)
简记。初始时,小团的位置在地图的左上角。即坐标 (1,1)
,地图上的每一个格子上都有一定的金币,特别地,小团位于的初始位置(1,1)上的金币为0。小团在进行探索移动时,可以选择向右移动一格(即从 (x, y)
到达 (x, y+1)
)或向下移动一格(即从 (x, y)
到达 (x+1,y)
)。地图上的每个格子都有一个颜色,红色或蓝色。如果小团一次移动前后的两个格子颜色不同,那么他需要支付k
个金币才能够完成这一次移动;如果移动前后的两个格子颜色相同,则不需要支付金币。小团可以在任意格子选择结束探索。
现在给你网格地图上每个格子的颜色与金币数量,假设小团初始时的金币数量为0,请你帮助小团计算出最优规划,使他能获得最多的金币,输出能获得的最多金币数量即可。
注意:要求保证小团任意时刻金币数量不小于零。
输入描述
第一行是三个用空格隔开的整数 n
,m
和 k
,表示网格地图的行数为 n
,列数为 m
,在不同颜色的两个格子间移动需要支付 k
个金币。
接下来 n
行,每行是一个长度为 m
的字符串,字符串仅包含字符 'R'
或 'B'
。第 i
行字符串的第 j
个字符表示地图上第 i
行第 j
列的格子颜色。如果字符为 'R'
,则表示格子颜色为红色,为 'B'
表示格子颜色为蓝色。
接下来是一个 n
行 m
列的非负整数矩阵,第 i
行第 j
列的数字表示地图上第 i
行第 j
列的格子上的金币数量。保证所有数据中数字大小都是介于 [0,10]
的整数。
1 ≤ n , m ≤ 200 1 \le n, m \le 200 1 ≤ n , m ≤ 200 , 1 ≤ k ≤ 5 1 \le k \le 5 1 ≤ k ≤ 5
输出描述
一个正整数,表示能获得最多金币的数量。
示例
1 2 3 4 5 6 7 8 9 10 3 3 2 BBB RBR RBR 0 2 3 2 4 1 3 2 2 8
代码 动态规划
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 const { readlines } = require ('../utils/read' );const [[n, m, k], ...lines] = readlines ();const colorMap = lines.slice (0 , n);const goldMap = lines.slice (n, n + n).map (line => line.map (Number ));console .log (n, m, k, colorMap, goldMap);const dp = Array .from ({ length : n }, () => Array .from ({ length : m }, () => -1 )); dp[0 ][0 ] = goldMap[0 ][0 ];for (let i = 1 ; i < n; i++) { const curColor = colorMap[i][0 ]; const upColor = colorMap[i - 1 ][0 ]; if (upColor === curColor || dp[i - 1 ][0 ] >= k) { dp[i][0 ] = dp[i - 1 ][0 ] + goldMap[i][0 ] - (upColor === curColor ? 0 : k); } }for (let j = 1 ; j < m; j++) { const curColor = colorMap[0 ][j]; const leftColor = colorMap[0 ][j - 1 ]; if (leftColor === curColor || dp[0 ][j - 1 ] >= k) { dp[0 ][j] = dp[0 ][j - 1 ] + goldMap[0 ][j] - (leftColor === curColor ? 0 : k); } }let maxDp = 0 ;for (let i = 1 ; i < n; i++) { for (let j = 1 ; j < m; j++) { const curColor = colorMap[i][j]; const leftColor = colorMap[i][j - 1 ]; const upColor = colorMap[i - 1 ][j]; let left2Cur = 0 ; let up2Cur = 0 ; if (dp[i][j - 1 ] !== -1 && (leftColor === curColor || dp[i][j - 1 ] >= k)) { left2Cur = dp[i][j - 1 ] + goldMap[i][j] - (leftColor === curColor ? 0 : k); } if (dp[i - 1 ][j] !== -1 && (upColor === curColor || dp[i - 1 ][j] >= k)) { up2Cur = dp[i - 1 ][j] + goldMap[i][j] - (upColor === curColor ? 0 : k); } dp[i][j] = Math .max (left2Cur, up2Cur); maxDp = Math .max (maxDp, dp[i][j]); } }console .log (dp);console .log (maxDp);
第四题:坦克大战 题目 小D和小W最近在玩坦克大战,双方操控自己的坦克在 16*16
的方格图上战斗,小D的坦克初始位置在地图的左上角,朝向为右,其坐标 (0, 0)
,小W的坦克初始位置在地图右下角,朝向为左,坐标为 (15, 15)
。坦克不能移动到地图外,坦克会占领自己所在的格子,己方坦克不可以进入对方占领过的格子。每一个回合双方必须对自己的坦克下达以下五种指令中的一种:
移动指令 U
:回合结束后,使已方坦克朝向为上,若上方的格子未被对方占领,则向当前朝向移动一个单位(横坐标 -1
),否则保持不动; 移动指令 D
:回合结束后,使己方坦克朝向为下,若下方的格子未被对方占领,则向当前朝向移动一个单位(横坐标 +1
),否则保持不动; 移动指令 L
:回合结束后,使己方坦克朝向为左,若左侧的格子未被对方占领,则向当前朝向移动一个单位(纵坐标 -1
),否则保持不动; 移动指令 R
:回合结束后,使己方坦克朝向为右,若右侧的格子未被对方占领,则向当前朝向移动一个单位(纵坐标 +1
),否则保特不动; 开火指令 F
:已方坦克在当前回合立即向当前朝向开火; 己方坦克开火后,当前回合己方坦克的正前方若有对方的坦克,对方的坦克将被摧毁,游戏结束,己方获得胜利;
若双方的坦克在同一回合被摧毁,游戏结束,判定为平局;若双方的坦克在同一回合内进入到同一个未被占领的格子,则双方的坦克发生碰撞,游戏结束,判定为平局;当游戏进行到第256个回合后,游戏结束,若双方坦克均未被摧毁,则占领格子数多的一方获得胜利,若双方占领的格子数一样多,判定为平局。注意 ,若一方开火,另一方移动,则认为是先开火,后移动。
现在小D和小W各自给出一串长度为256的指令字符串,请你帮助他们计算出游戏将在多少个回合后结束,以及游戏的结果。
示例
1 2 3 4 5 DDDDDDDRF UUUUUUUUU 9 D
代码 模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 const { readlines } = require ('../utils/read' );const [dInstru, wInstru] = readlines ();const grid = Array .from ({ length : 16 }, () => Array .from ({ length : 16 }, () => -1 )); grid[0 ][0 ] = 1 ; grid[15 ][15 ] = -1 ;const curPos = [[0 , 0 ], [15 , 15 ]];const ds = [[0 , 1 ], [0 , -1 ]];const cnts = [1 , 1 ];const dirs = { 'U' : [-1 , 0 ], 'D' : [1 , 0 ], 'L' : [0 , -1 ], 'R' : [0 , 1 ] };const move = (instru, p ) => { const dir = dirs[instru]; let [i, j] = curPos[p]; [i, j] = [i + dir[0 ], j + dir[1 ]]; ds[p] = dir; if (grid[i][j] === -1 || grid[i][j ] === p) { curPos[p][0 ] += dir[0 ]; curPos[p][1 ] += dir[1 ]; } if (grid[i][j] === -1 ) { grid[i][j] = p; cnts[p]++; } };const fire = (p1, p2 ) => { if (ds[p1][0 ] === -1 && curPos[p2][1 ] === curPos[p1][1 ] && curPos[p2][0 ] < curPos[p1][0 ]) return true ; if (ds[p1][0 ] === 1 && curPos[p2][1 ] === curPos[p1][1 ] && curPos[p2][0 ] > curPos[p1][0 ]) return true ; if (ds[p1][1 ] === -1 && curPos[p2][0 ] === curPos[p1][0 ] && curPos[p2][1 ] < curPos[p1][1 ]) return true ; if (ds[p1][1 ] === 1 && curPos[p2][0 ] === curPos[p1][0 ] && curPos[p2][1 ] > curPos[p1][1 ]) return true ; return false ; };const main = ( ) => { for (let i = 0 ; i < dInstru.length ; i++) { const di = dInstru[i]; const wi = wInstru[i]; if (di === 'F' && wi === 'F' && fire (0 , 1 ) && fire (1 , 0 )) { console .log (i + 1 , '平局' ); return ; } if (di === 'F' && fire (0 , 1 )) { console .log (i, 'D 胜' ); return ; } if (wi === 'F' && fire (1 , 0 )) { console .log (i, 'W 胜' ); return ; } if (di !== 'F' ) move (di, 0 ); if (wi !== 'F' ) move (wi, 1 ); if (curPos[0 ][0 ] === curPos[1 ][0 ] && curPos[0 ][1 ] === curPos[1 ][1 ]) { console .log (i, '平局' ); return ; } } if (cnts[0 ] === cnts[1 ]) { console .log (256 , '平局' ); return ; } if (cnts[0 ] > cnts[1 ]) { console .log (256 , 'D 胜' ); return ; } console .log (256 , 'W 胜' ); };main ();