بطولة البرمجة: تحليل المهام للمطورين الأمامي

في اليوم الآخر ، حصل الفائزون في بطولة البرمجة ، والتي انتهت في أوائل الصيف ، على جوائز تستحقها عن جدارة. للقيام بذلك ، اتصلنا بهم ، وكذلك جميع المتأهلين للتصفيات النهائية من أعلى 20 من كل اتجاه إلى مكتب ياندكس موسكو. مرة أخرى ، تهانينا لأولئك الذين تمكنوا من الوصول إلى النهائي.

في غضون ذلك ، قمنا بإعداد مناقشة مهام البطولة التي تم تقديمها للمطورين المتقدمين. هذه هي المهام من مرحلة التأهيل. نذكرك أن البطولة عقدت في أربعة مجالات: الخلفية ، الواجهة الأمامية ، التعلم الآلي والتحليلات.

أ. ميزان الحرارة


حالة


باستخدام الملاح ، رأى الكثيرون "مقياس حرارة" عند بناء طريق السيارة. إنه خط مستقيم متعدد الألوان ، مما يدل على ازدحام الطرق على الطريق. في هذه المهمة ، يُقترح كتابة وظيفة تتكيف مع بيانات "مقياس الحرارة" لأحجام مختلفة للشاشة.

يتم إعطاء مدخلات الوظيفة مجموعة من ألوان طول length width حجم الشاشة ( length ≥ width ) الذي سيتم عرض مقياس الحرارة عليه. تتوافق ألوان GREEN و YELLOW و RED مع الأحمال المنخفضة والمتوسطة والعالية على التوالي. الألوان قابلة للمقارنة من حيث حركة المرور: GREEN < YELLOW < RED .

يتم تقسيم الصفيف الأولي ، بدءًا من العنصر الأول ، إلى صفيفات فرعية مفككة متتالية length / width طول length / width (سيكون الرقم دائمًا عددًا صحيحًا). في كل طبقة فرعية ، من الضروري ترتيب الألوان وفقًا لدرجة تزايد الازدحام في الطرق ، واختيار اللون المتوسط ​​واستبدال المجموعة بأكملها بها. في حالة صفيف ذي طول متساوي ، يتم تحديد "الوسيط الأدنى" (العنصر n/2 في صف مرتبة من العناصر n ). يجب أن تكون النتيجة مجموعة من ألوان width الطول.

يجب توفير الحل كوحدة نمطية لـ CommonJS:

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

حكم RE يعني أيضًا أن الحل المقدم غير صحيح.

خيارات إضافية
دخولاستنتاج
 const segments = ['GREEN', 'GREEN', 'RED', 'GREEN', 'YELLOW', 'RED', 'GREEN', 'YELLOW', 'RED', 'YELLOW']; const width = 5; 
['GREEN', 'GREEN', 'YELLOW', 'GREEN', 'YELLOW']

قرار


1. قم بتقسيم المجموعة الأولية من المقاطع إلى مقاطع length / width .
2. في كل قطعة ، حدد اللون المتوسط ​​، بناءً على الحالة ، وأضف اللون الموجود إلى الصفيف الناتج.

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. تورنت العميل


حالة


قررت أن تكتب عميل سيل. ستكون الميزة أنه من خلال مساعدتها ، يمكن إرسال النص فقط.

عميل سيل جاهز تقريبًا ، يبقى الشيء الأكثر أهمية هو: جمع نص المصدر من الأجزاء التي تم تقسيمها لنقلها.

اكتب وظيفة تنتظر تحميل جميع أجزاء النص وجمع المصدر منها.

تقبل الدالة كائنًا به حقلان: chunkCount و chunkCount ، وتقوم بإرجاع وعد يحتوي إما على النص المصدر أو خطأ في شكل سلسلة بالتنسيق المحدد.

chunkCount - عدد القطع التي تم تقسيم النص إليها.

كل جزء من النص لديه معرف فريد ووقت الإرسال. توجد القطع ذات وقت الإرسال لاحقًا من بداية النص.

emitter - كائن يمكنك من خلاله تنزيل أجزاء من النص. قد تصل أجزاء النص مع تأخير زمني تعسفي. ترتيب القطع يمكن أن يكون أي.

إذا تم استلام نفس النص مرتين قبل اكتمال التنزيل بنجاح ، فيجب أن ترمي الوظيفة خطأ "Duplicate: <id>" (بمعرف جزء النص بدلاً من <id> ).

بمجرد استلام جميع أجزاء النص ، من الضروري دمجها في سطر واحد وإرجاع هذا السطر باستخدام وعد. إذا كان للقطعتين نفس أوقات الإرسال ، فيمكن أن يكون ترتيب هذه القطع في السلسلة التي تم إرجاعها هو أي.

في حالة عدم اكتمال عملية النقل خلال ثانية واحدة ، يجب أن ترمي الدالة خطأ "Timed out" .

يتوافق الإدخال مع واجهة مثل TypeScript
( وصف عام لواجهات TS.)

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


يجب توفير الحل كوحدة نمطية لـ CommonJS:

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

حكم RE يعني أيضًا أن الحل المقدم غير صحيح.

خيارات إضافية
أمثلة
دخولاستنتاج
 { 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"

قرار


  • حفظ القطع المحملة إلى كائن chunk .
  • في الوقت نفسه ، نتحقق من وجود id . إذا كان موجودًا بالفعل ، فقم بإلغاء الوعد.
  • بعد تحميل جميع القطع ، وفرزها ودمجها.
  • بالتوازي مع هذا ، تحتاج إلى تعيين مهلة 1 ثانية.


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. شجرة ثنائية


حالة


تم تكليف المطور Grisha بمهمة تنفيذ شجرة ثنائية ، لكنه لم يفهم الجوهر وارتكب الكثير من الأخطاء. ساعده في العثور عليها وإصلاحها.

من الضروري إيجاد وإصلاح الأخطاء في رمز task.js يجب تصدير الفصل للعمل مع شجرة ثنائية. واجهة الطبقة:

 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; } 

ملاحظة : النظر في JSDoc الصحيح.

حكم RE يعني أيضًا أن الحل المقدم غير صحيح.

خيارات إضافية
مثال المدخلات :
 let output = ''; BinaryTreeNode.create(10, 5, 13, 7, 20, 12).inorder((data) => { output += data + '-'; }); 

الخلاصة :
 5-7-10-12-13-20- 

قرار


 /** * @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 الشعار


حالة


قام المصمم بتحديث شعار Yandex.Maps (مقياس x5):



سوف تحتاج إلى استخدامها في مجموعة متنوعة من الظروف. لجعلها مريحة قدر الإمكان ، اصنعها بعنصر HTML واحد في CSS خالص. يمكن استخدام الشعار في أي مكان من الواجهة ، لذلك من المهم أن يتم عرضه بشكل صحيح على أي خلفية.

لا يمكنك استخدام الصور (حتى من خلال data:uri ).

خيارات إضافية

- ألوان مركز الدائرة: #fff
- لون الدائرة الخارجية: # f33
- لون "الساقين": # e00000

يجب توفير الحل كملف CSS. سيتم توصيل الملف الخاص بك كحل. solution.css إلى صفحة HTML من النموذج:

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

هام : يجب أن يكون الشعار في الركن العلوي الأيسر من الصفحة ، مع الضغط عليه عن كثب.

سيتم اختبار الحل في متصفح Google Chrome 69 .

نوصي باستخدام مكونات إضافية لتخطيطات بكسل مثالية ، مثل PerfectPixel .

قرار


 //          . 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. شبكة الطوب


حالة


قرر المطور Ivan إعادة تشكيل أنماط CSS للصفحة ، وبعد ذلك كسر مظهرها.

التصميم الأولي:

تحتاج إلى جعل الشكل يتماشى مع التصميم الأصلي مع أقل عدد من التغييرات في ملف CSS الحالي.

هام : عند إضافة عناصر إلى القائمة ، يجب أن تنمو الشبكة بشكل مشابه.

أنماط CSS بعد إعادة ./solution.css : ./solution.css .

بعد التصحيحات ، تحتاج إلى توفير ملف CSS محدث. سيتم توصيل هذا الملف كحل ثابت. solution.css إلى صفحة HTML .

خيارات إضافية
سيتم اختبار الحل في متصفح Google Chrome 69 . عائلة الخط وإعدادات الخط الأخرى لا تحتاج إلى تغيير. في هذه الحالة ، محليًا ، قد لا يتطابق الخط مع الحالة المتوقعة ، لأن لقطات الشاشة قد تم التقاطها في أوبونتو.

نوصي باستخدام مكونات إضافية لتخطيطات بكسل مثالية ، مثل PerfectPixel .

قرار


يجب إجراء التغييرات فقط مع محدد .event وأحفاده.

 :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); } 

واو ركوب المترو


حالة


هناك ديفوبيا بيتيا. في العمل ، يجب أن يكون في الخدمة في أيام معينة لمدة 100 يوم. بيتيا يحصل على العمل عن طريق المترو. تم تقديم تذاكر مترو أنفاق صالحة لعدد معين من الأيام من الرحلة الأولى عليها. كلما طالت مدة صلاحية التذكرة ، انخفضت التكلفة في اليوم. من الضروري مساعدة Petya في توفير المال وحساب التذاكر التي يحتاجها لشراء ثلاثة أشهر مقدمًا ، مع مراعاة جدول واجباته ، بحيث تكون التكلفة الإجمالية منخفضة قدر الإمكان. ولا يحب Petya أن يحمل معه العديد من التذاكر ، وإذا كان هناك العديد من خيارات التذاكر بنفس التكلفة الدنيا ، فإن Petya يحتاج إلى واحد مع عدد أقل من التذاكر. إذا كان هناك العديد من هذه الخيارات (مع نفس الحد الأدنى للتكلفة وعدد التذاكر) ، فإن بيت سوف يناسب أي منها.

تحتاج إلى كتابة getCheapestTickets(days, tickets) ، والتي تأخذ جدول مهام getCheapestTickets(days, tickets) وخيارات التذاكر الممكنة ( tickets ) كمدخلات ، وتعطي قائمة بالتذاكر (في شكل مؤشرات من مجموعة مدخلات من خيارات التذاكر) التي تحتاج إلى شرائها بيت.

يتم تقديم جدول مهام Petya في شكل مجموعة مرتبة من الأرقام (من 1 إلى 100 شاملة) ، يشير كل منها إلى العدد الترتيبي ليوم العمل:

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

يتم وصف كل تذكرة بالواجهة التالية:

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

لا يزيد عدد خيارات التذاكر عن 10 خيارات ، ويضمن أن تكون جميع التذاكر بأسعار مختلفة ، وكلما زاد عدد أيام صلاحية البطاقة ، انخفضت تكلفتها في يوم واحد.

يجب توفير الحل كوحدة نمطية لـ CommonJS:

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

حكم RE يعني أيضًا أن الحل المقدم غير صحيح.

خيارات إضافية
أمثلة

دخولاستنتاج
 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]

في اليومين الأول والثاني ، تحتاج Petya إلى شراء تذاكر ليوم واحد ، في اليوم الرابع هو سبعة أيام ، في اليوم العشرين ليوم واحد مرة أخرى.

ستكون التكلفة الإجمالية لهذه التذاكر هي الأقل الممكنة: 19 .

قرار


أحد الحلول الممكنة هي طريقة البرمجة الديناميكية ، وهي:

1. خذ اليوم الأول من العمل Petit.
2. للعثور على أفضل الحلول لهذا اليوم ، نقوم بحساب الخيارات الممكنة لكل تذكرة. يتكون كل خيار من هذه الخيارات من تكلفة التذكرة وتكلفة أفضل حل في يوم العمل التالي لتاريخ انتهاء صلاحية هذه التذكرة. يتم احتساب المصطلح الثاني بالمثل ، وبالتالي الحصول على العودية.
3. بالإضافة إلى ذلك ، النظر في عدد التذاكر ، إذا كان هناك العديد من هذه الخيارات.
4. يجب إيلاء اهتمام خاص لحلول التخزين المؤقت في الأيام المؤقتة.

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; }; 



هنا رابط لتحليل المهام لمطوري الواجهة الخلفية.

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


All Articles