Campeonato de programación: tareas de análisis para desarrolladores front-end

El otro día, los ganadores del campeonato de programación, que terminó a principios del verano, recibieron merecidos premios. Para hacer esto, los llamamos, así como a todos los otros finalistas de los 20 mejores de cada dirección a la oficina de Yandex Moscú. Una vez más, felicidades a quienes lograron llegar a la final.

Mientras tanto, preparamos una discusión sobre las tareas de campeonato que se ofrecieron a los desarrolladores front-end. Estas son tareas de la etapa de calificación. Le recordamos que el campeonato se celebró en cuatro áreas: backend, frontend, aprendizaje automático y análisis.

A. termómetro


Condición


Usando el navegador, muchos vieron un "termómetro" cuando construían una ruta de automóvil. Es una línea recta multicolor que muestra la congestión de carreteras en la ruta. En esta tarea, se propone escribir una función que adapte los datos del "termómetro" para diferentes tamaños de pantalla.

La entrada de función recibe una matriz de colores de longitud, length y width pantalla ( length ≥ width ) en la que se mostrará el termómetro. Los colores GREEN , YELLOW y RED corresponden a carga baja, media y alta, respectivamente. Los colores son comparables en términos de tráfico: GREEN < YELLOW < RED .

La matriz inicial, comenzando desde el primer elemento, se divide en sucesivas submatrices disjuntas de longitud length / width (el número siempre será un número entero). En cada subconjunto, es necesario organizar los colores de acuerdo con el grado creciente de congestión de las carreteras, elegir el color mediano y reemplazar todo el conjunto con él. En el caso de una matriz de longitud par, se selecciona la "mediana inferior" (elemento n/2 en una fila ordenada de n elementos). El resultado debería ser una variedad de colores de largo width .

La solución debe proporcionarse como un módulo CommonJS:

 module.exports = function (segments, width) { // Your code here. }; 

El veredicto RE también significa que la solución presentada es incorrecta.

Opciones adicionales
EntrarConclusión
 const segments = ['GREEN', 'GREEN', 'RED', 'GREEN', 'YELLOW', 'RED', 'GREEN', 'YELLOW', 'RED', 'YELLOW']; const width = 5; 
['GREEN', 'GREEN', 'YELLOW', 'GREEN', 'YELLOW']

Solución


1. Divida la matriz inicial de segmentos en segmentos de length / width .
2. En cada segmento, seleccione el color mediano, según la condición, y agregue el color encontrado a la matriz resultante.

solution.js
 module.exports = function solution(segments, width) { const blockSize = segments.length / width; const result = []; for (let i = 0; i < width; i++) { result.push(getMedian(segments.slice(i * blockSize, (i + 1) * blockSize))); } return result; }; function getMedian(array) { const map = { GREEN: 1, YELLOW: 2, RED: 3 }; return array.sort((a, b) => map[a] - map[b])[Math.floor((array.length - 1) / 2)]; } 

B. Cliente Torrent


Condición


Decidiste escribir a tu cliente torrent. Su característica será que con su ayuda es posible transmitir solo texto.

El cliente de torrent está casi listo, lo más importante sigue siendo: recopilar el texto fuente de las piezas en las que se dividió para su transmisión.

Escriba una función que espere a que se carguen todos los fragmentos de texto y recopile la fuente de ellos.

La función toma un objeto de entrada con dos campos: chunkCount y chunkCount , y devuelve una promesa que contiene el texto fuente o un error en forma de una cadena del formato especificado.

chunkCount : la cantidad de piezas en las que se dividió el texto.

Cada texto tiene un identificador único y un tiempo de envío. Las piezas con un tiempo de envío posterior se encuentran más lejos desde el comienzo del texto.

emitter : un objeto con el que puede obtener fragmentos de texto descargados. Los fragmentos de texto pueden llegar con retrasos arbitrarios. El orden de las piezas puede ser cualquiera.

Si se recibe el mismo texto dos veces antes de que la descarga se haya completado con éxito, la función debería arrojar un error "Duplicate: <id>" (con la identificación del texto en lugar de <id> ).

Una vez que se han recibido todos los fragmentos de texto, es necesario combinarlos en una sola línea y devolver esta línea con una promesa. Si dos piezas tienen los mismos tiempos de envío, el orden de estas piezas en la cadena devuelta puede ser cualquiera.

Si la transferencia no se completa en un segundo, la función debería arrojar un error de "Timed out" .

La entrada corresponde a dicha interfaz en TypeScript
( Descripción general de las interfaces TS).

 interface Input { chunkCount: number; emitter: Emitter; } interface Emitter { on: (callback: (chunk: Chunk) => void) => void; } interface Chunk { id: number; timestamp: Date; data: string; } 


La solución debe proporcionarse como un módulo CommonJS:

 module.exports = function ({chunkCount, emitter}) { //  Promise }; 

El veredicto RE también significa que la solución presentada es incorrecta.

Opciones adicionales
Ejemplos
EntrarConclusión
 { chunkCount: 3, emitter: {on: (fn) => { fn({id: 5314, data: 'The Good, ', timestamp: new Date('2019-01-01')}); fn({id: 1543, data: 'd the Ugly', timestamp: new Date('2019-01-03')}); fn({id: 2494, data: 'the Bad an', timestamp: new Date('2019-01-02')}); }} } 
Resolved with "The Good, the Bad and the Ugly"
 { chunkCount: 1, emitter: {on: (fn) => { fn({id: 0, data: 'hello', timestamp: new Date('2019-01-01')}); fn({id: 0, data: 'world', timestamp: new Date('2019-01-02')}); }} } 
Rejected with "Duplicate id: 0"
 { chunkCount: 2, emitter: {on: (fn) => {}} } 
Rejected with "Timed out"

Solución


  • Guarde los fragmentos cargados en un objeto chunk .
  • En este caso, verificamos la existencia de id . Si ya existe, cancele la promesa.
  • Después de cargar todas las piezas, ordenarlas y combinarlas.
  • Paralelamente a esto, debe establecer un tiempo de espera de 1 s.


solution.js
 module.exports = function ({chunkCount, emitter: {on}}) { return new Promise((resolve, reject) => { const chunks = {}; let chunksDownloaded = 0; on(({id, data, timestamp}) => { if (typeof chunks[id] !== 'undefined') { reject(`Duplicate: ${id}`); } else { chunks[id] = {data, timestamp}; chunksDownloaded += 1; if (chunksDownloaded === chunkCount) { const result = Object.values(chunks) .sort((a, b) => a.timestamp - b.timestamp) .map(({data}) => data) .join(''); resolve(result); } } }); setTimeout(() => { reject('Timed out'); }, 1000); }); }; 

C. árbol binario


Condición


Al desarrollador Grisha se le encomendó la tarea de implementar un árbol binario , pero entendió mal la esencia y cometió muchos errores. Ayúdelo a encontrarlos y arreglarlos.

Es necesario encontrar y corregir errores en el código task.js Se debe exportar una clase para trabajar con un árbol binario. Interfaz de clase:

 type Data = number; type ITraverseCallback = (data: Data) => void; interface IBinaryTreeNode { data: Data; left: IBinaryTreeNode | null; right: IBinaryTreeNode | null; static create(...items: Data[]): IBinaryTreeNode; constructor(data: Data); insert(data: Data): this; remove(data: Data): IBinaryTreeNode | null; search(data: Data): IBinaryTreeNode | null; inorder(callback: ITraverseCallback): this; preorder(callback: ITraverseCallback): this; postorder(callback: ITraverseCallback): this; } 

Nota : Considere JSDoc el correcto.

El veredicto RE también significa que la solución presentada es incorrecta.

Opciones adicionales
Ejemplo de entrada :
 let output = ''; BinaryTreeNode.create(10, 5, 13, 7, 20, 12).inorder((data) => { output += data + '-'; }); 

Conclusión
 5-7-10-12-13-20- 

Solución


 /** * @typedef Data * @type {Number} */ class BinaryTreeNode { /** * @param {...Data} items * @returns {BinaryTreeNode} */ static create(...items) { // e - . const root = new BinaryTreeNode(items[0]); //  return   . //  .slice(1),     . return items.slice(1).reduce((node, data) => node.insert(data), root); } /** * @param {Data} data */ constructor(data) { /** * @type {Data} */ this.data = data; //    . /** * @type {BinaryTreeNode | null} */ this.left = null; /** * @type {BinaryTreeNode | null} */ this.right = null; } /** *    . *    ,      . * * @param {Date} data * @returns {BinaryTreeNode} */ insert(data) { //    . if (data < this.data) { if (this.left === null) { this.left = new BinaryTreeNode(data); } else { this.left.insert(data); } } else { if (this.right === null) { this.right = new BinaryTreeNode(data); } else { this.right.insert(data); } } //    ,   . return this; } /** *     . *   ,   . * * @param {Data} data * @returns {BinaryTreeNode | null} */ remove(data) { //     {}. //    . if (data < this.data) { //     `this.left`. this.left = this.left && this.left.remove(data); } else if (data > this.data) { //     `this.right`. this.right = this.right && this.right.remove(data); } else { if (this.left === null && this.right === null) { return null; } if (this.left === null) { return this.right; } else if (this.right === null) { return this.left; } const aux = findMinNode(this.right); this.data = aux.data; this.right = this.right.remove(aux.data); } //    ,   . return this; } /** *     . * * @param {Data} data * @returns {BinaryTreeNode | null} */ search(data) { //    . if (data < this.data) { //     `this.left`. return this.left && this.left.search(data); } if (data > this.data) { //     `this.right`. return this.right && this.right.search(data); } //  ,     ,    `null`. if (data === this.data) { return this; } return null; } /** *    ,           . *     . * * @param {Function} callback * @returns {BinaryTreeNode} */ inorder(callback) { if (this.left !== null) { this.left.inorder(callback); } callback(this.data); if (this.right !== null) { this.right.inorder(callback); } //    ,   . return this; } /** *   ,           . * * @param {Function} callback * @returns {BinaryTreeNode} */ preorder(callback) { callback(this.data); if (this.left !== null) { //       . this.left.preorder(callback); } if (this.right !== null) { this.right.preorder(callback); } //    ,   . return this; } /** *   ,            . * * @param {Function} callback * @returns {BinaryTreeNode} */ postorder(callback) { if (this.left !== null) { this.left.postorder(callback); } if (this.right !== null) { //       . this.right.postorder(callback); } //   ,     . callback(this.data); return this; } } /** *   ,   . * * @param {BinaryTreeNode} node * @returns {BinaryTreeNode} */ function findMinNode(node) { //       . //    true  false. if (node.left === null) { return node; } else { return findMinNode(node.left); } } module.exports = BinaryTreeNode; 

D. Yandex.Maps logo


Condición


El diseñador ha actualizado el logotipo de Yandex.Maps (escala x5):



Tendrá que usarse en una variedad de condiciones. Para hacerlo lo más conveniente posible, complételo con un elemento HTML en CSS puro. El logotipo se puede usar en cualquier lugar de la interfaz, por lo que es importante que se muestre correctamente en cualquier fondo.

No puede usar imágenes (incluso a través de data:uri ).

Opciones adicionales

- Colores del círculo central: #fff
- El color del círculo exterior: # f33
- El color de las "piernas": # e00000

La solución debe proporcionarse como un archivo CSS. Su archivo se conectará como solution.css a una página HTML del formulario:

 <!DOCTYPE html> <html> <head> <style> body { margin: 0; } </style> <link rel="stylesheet" href="solution.css"> </head> <body> <div></div> </body> </html> 

Importante : el logotipo debe ubicarse en la esquina superior izquierda de la página, presionado estrechamente.

Su solución se probará en el navegador Google Chrome 69 .

Recomendamos el uso de complementos para diseños con píxeles perfectos, como PerfectPixel .

Solución


 //          . div { position: absolute; width: 6px; height: 6px; border: 5px solid #f33; border-radius: 8px; background: #fff; } //     «» . //    ,      9 . div::after { content: ''; position: absolute; top: 6px; left: 2px; border-top: 15px solid #e00000; border-right: 7px solid transparent; transform: rotate(9deg); z-index: -1; } 


E. malla de ladrillo


Condición


El desarrollador Ivan decidió refactorizar los estilos CSS de la página, después de lo cual rompió su apariencia.

Diseño inicial:

Debe alinear el aspecto con el diseño original con el menor número de cambios en el archivo CSS actual.

Importante : Al agregar elementos a la lista, la cuadrícula debería crecer de manera similar.

Estilos CSS después de refactorizar: ./solution.css .

Después de las correcciones, debe proporcionar un archivo CSS actualizado. Este archivo se conectará como una solution.css fija.css a la página HTML .

Opciones adicionales
Su solución se probará en el navegador Google Chrome 69 . No es necesario cambiar la familia de fuentes y otras configuraciones de fuentes. En este caso, localmente, la fuente puede no coincidir con el estado esperado, porque las capturas de pantalla se tomaron en Ubuntu.

Recomendamos el uso de complementos para diseños con píxeles perfectos, como PerfectPixel .

Solución


Los cambios solo deben hacerse con el selector .event y sus descendientes.

 :root { --color-gray: #4e4d4d; --color-main: #000000; --width-layout: 900px; --paddingx5: 50px; --paddingx4: 40px; --paddingx3: 30px; --paddingx2: 20px; --padding: 10px; --font-size-largex2: 40px; --font-size-large: 20px; --font-size-medium: 16px; --font-size-small: 14px; } body { margin: 0 auto; padding: var(--paddingx5) var(--paddingx4); font: var(--font-size-small)/1.4 arialregular; color: var(--color-main); width: var(--width-layout); } .hgroup { margin-bottom: var(--paddingx4); text-align: center; } .hgroup__title { font-size: var(--font-size-largex2); font-weight: normal; margin: 0; } .hgroup__desc { font-size: var(--font-size-large); font-weight: normal; color: var(--color-gray); margin: 0; } .events { list-style: none; margin: 0; padding: 0; //    . //      . columns: 3; column-gap: var(--paddingx4); } .events__item { //    . break-inside: avoid; //  margin     . padding-bottom: var(--paddingx4); } .card { text-decoration: none; color: var(--color-main); display: block; } .card:hover .card__title { text-decoration: underline; } .card__image { width: 100%; display: block; height: 100px; background: var(--color-gray); margin-bottom: var(--padding); } .card__title { margin: 0 0 var(--padding); } .card__summary { margin: 0; color: var(--color-gray); } 

F. Paseos en metro


Condición


Ahí está Devopia Petya. En el trabajo, debe estar de guardia en ciertos días durante los próximos 100 días. Petya llega al trabajo en metro. Se introdujeron boletos de metro que son válidos por un cierto número de días desde el primer viaje en ellos. Cuanto más tiempo sea válido el boleto, menor será el costo por día. Necesitamos ayudar a Petya a ahorrar dinero y calcular qué boletos necesita comprar con tres meses de anticipación, teniendo en cuenta el horario de sus deberes, para que su costo total sea lo más bajo posible. Y a Petya no le gusta llevar muchos boletos con él, y si hay varias opciones de boletos con el mismo costo mínimo, entonces Petya necesita uno con menos boletos. Si hay varias de esas opciones (con el mismo costo mínimo y número de boletos), entonces Pete se adaptará a cualquiera de ellas.

getCheapestTickets(days, tickets) escribir una función getCheapestTickets(days, tickets) que tome el horario de servicio de Petya ( days ) y las posibles opciones de boletos como entrada y proporcione una lista de boletos (en forma de índices de la matriz de entrada de opciones de boletos) que necesita comprar Pete

El horario de servicio de Petya se da en forma de una serie ordenada de números (del 1 al 100 inclusive), cada uno de los cuales indica el número ordinal del día de servicio:

 [2, 5, 10, 45] //     , ,        . 

Cada ticket se describe en la siguiente interfaz:

 interface Ticket { duration: number; //  ,           ,    ( 1  100 ) cost: number; //   ( 1  100 ) } 

El número de opciones de boletos no es más de 10, y se garantiza que todos los boletos tengan precios diferentes, y cuantos más días sea válido, menor será su costo en términos de un día.

La solución debe proporcionarse como un módulo CommonJS:

 module.exports = function (days, tickets) { // Your code here. }; 

El veredicto RE también significa que la solución presentada es incorrecta.

Opciones adicionales
Ejemplos

EntrarConclusión
 const days = [1, 2, 4, 6, 7, 8, 9, 10, 20]; const tickets = [ { cost: 3, duration: 1 }, { cost: 10, duration: 7 }, { cost: 20, duration: 30 } ]; 
[0, 0, 1]

En el primer y segundo día, Petya necesita comprar boletos de un día, el cuarto día son boletos de siete días, el vigésimo día, un día más.

El costo total de dichos boletos será el más bajo posible: 19 .

Solución


Una de las posibles soluciones es el método de programación dinámica, a saber:

1. Tome el primer día de servicio Petit.
2. Para encontrar la mejor solución para este día, calculamos las posibles opciones para cada uno de los boletos. Cada una de estas opciones consiste en el costo del boleto y el costo de la mejor solución el día de servicio después de la fecha de vencimiento de este boleto. El segundo término se calcula de manera similar, obteniendo así una recursión.
3. Además, considere la cantidad de boletos, si hay varias de esas opciones.
4. Se debe prestar especial atención a las soluciones de almacenamiento en caché en los días intermedios.

solution.js
 module.exports = function (days, tickets) { if (days.length === 0 || tickets.length === 0) { return []; } tickets = tickets .map((ticket, idx) => ({ ...ticket, idx })) .sort((a, b) => a.duration - b.duration); const daysSolutions = new Map(); function getDaySolution(idx) { if (daysSolutions.has(idx)) { return daysSolutions.get(idx); } const solution = { totalCost: Number.POSITIVE_INFINITY, totalTickets: Number.POSITIVE_INFINITY, ticketToBuy: null, next: null }; for (let i = 0, j = idx; i < tickets.length && j < days.length; i++) { while (j < days.length && days[j] < days[idx] + tickets[i].duration) { j++; } const nextDaySolution = j < days.length ? getDaySolution(j) : null; let totalCost = tickets[i].cost; let totalTickets = 1; if (nextDaySolution) { totalCost += nextDaySolution.totalCost; totalTickets += nextDaySolution.totalTickets; } if ( totalCost < solution.totalCost || (totalCost === solution.totalCost && totalTickets < solution.totalTickets) ) { solution.totalCost = totalCost; solution.totalTickets = totalTickets; solution.ticketToBuy = tickets[i].idx; solution.next = nextDaySolution; } } daysSolutions.set(idx, solution); return solution; } let solution = getDaySolution(0); const res = []; while (solution) { res.push(solution.ticketToBuy); solution = solution.next; } return res; }; 



Aquí hay un enlace a las tareas de análisis para desarrolladores de backend.

Source: https://habr.com/ru/post/460139/


All Articles