Given an array of numbers:

1 2 3 4 6 3 2 4 7 3 4 6 1 2 1

Each number in this array indicates height. The combination of heights forms a two-dimensional landscape.

enter image description here

Imagine that it is raining and water accumulates in the pits of this landscape. It is necessary to calculate how many cells the water fills. enter image description here

The ideal solution to this problem involves passing through the array once.

Implement on js with the input of an array of numbers and schematic visualization of the landscape, as well as the output of the result.

Graphic output made via chart.js http://prntscr.com/mthgtk

Here is a solution

HTML

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="./data.js"></script> </head> <style> #app { display: flex; padding-top: 100px; padding-left: 200px; } .cube { height: 50px; width: 50px; background-color: gray; margin:1px; display: flex; align-items: center; justify-content: center; font-size: 20px; font-weight: bold; color: brown; } .column { display: flex; flex-direction: column-reverse; } .water { height: 50px; width: 50px; background-color: rgb(26, 123, 187); margin:1px; display: flex; align-items: center; justify-content: center; font-size: 20px; font-weight: bold; color: brown; } </style> <body> <div id="app"> </div> </body> </html> 

Js

 window.onload = () => { const app = document.getElementById('app'); const coors = [1, 2, 3, 4, 6, 3, 2, 4, 7, 3, 4, 6, 1, 2, 1]; const highest = coors.map(v => v).sort().reverse()[0]; for (const el of coors) { let col = createColumn(); for (let i = 0; i < el; i++) { const cube = createCube(); cube.innerHTML = i + 1; col.appendChild(cube); } for (let j = 0; j < highest - el; j++) { const cube = createWater(); cube.innerHTML = j + 1; col.appendChild(cube); } app.appendChild(col); } var colums = Array.from(app.getElementsByClassName('column')); var copy = Object.assign({}, colums); for (let index = 0; index < colums.length; index++) { if (colums[index - 1]) { const groundLeft = colums[index - 1].getElementsByClassName('cube').length; const groundCenter = colums[index].getElementsByClassName('cube').length; if ((groundLeft > groundCenter)) { Array .from(colums[index].getElementsByClassName('water')) .forEach(v => colums[index].removeChild(v)); } if ((groundLeft < groundCenter)) { Array .from(colums[index - 1].getElementsByClassName('water')) .forEach(v => colums[index - 1].removeChild(v)); } } } let picks = colums.map((v, i, a) => { const ind = Array.from(v.children).findIndex(z => z.classList.contains('water')); if ( ind> -1) { return { index: i, height: v.getElementsByClassName('cube').length }; } }); const picksAll = picks.filter(v => v).map((v, i, array) => { return { first: array[i], next: array[i + 1] } }).filter(v => v.first && v.next); for (const pick of picksAll) { const height = pick.first.height > pick.next.height ? pick.next.height : pick.first.height; const start = pick.first.index + 1; for (let i = start; i < pick.next.index; i++) { const need = height - copy[i].children.length; for (let j = 0; j < need; j++) { copy[i].appendChild(createWater()); } } Array.from(copy[pick.first.index].children).forEach((v, i, a) => { if (v.classList.contains('water')) { copy[pick.first.index].removeChild(v); } }); Array.from(copy[pick.next.index].children).forEach((v, i, a) => { if (v.classList.contains('water')) { copy[pick.next.index].removeChild(v); } }); } } function createColumn() { const column = document.createElement('div'); column.className = 'column'; return column; } function createCube() { const column = document.createElement('div'); column.className = 'cube'; return column; } function createWater() { const column = document.createElement('div'); column.className = 'water'; return column; } 

enter image description here

  • Comments are not intended for extended discussion; conversation moved to chat . - Yuriy SPb ♦

2 answers 2

Solution in C ++ (but in general it is not important, the code +/- is the same). As a demonstration, I only output an answer (it is clear what to fill out). Running example https://ideone.com/M9QHpf

 int v[] = {1, 2, 3, 4, 6, 3, 2, 4, 7, 3, 4, 6, 1, 2, 1}; int count = 0; for (int l=0,r=sizeof(v)/sizeof(v[0]),lM = v[0], rM = v[sizeof(v)/sizeof(v[0])]; l < r; lM = max(lM,v[l]), rM = max(rM,v[r])) if(lM >= rM) count += rM - v[r--]; else count += lM - v[l++]; cout << count<<endl; 

Now what's the magic? If we raise the water level, it means that the maximum on the left and the maximum on the right is greater than our current value. (maximum can not be less than the current). Actually to the minimum of the maximum we can add. But there is one thing, we do it in 1 pass and not in 2, so you need to carefully hold 2 pointers to the highs and move the smaller

  • laid out the problem like this prntscr.com/mtf30h , the difficulty arises only with the understanding of how to calculate the "water". - mobius
  • Well, actually all the code is written ... - pavel
  • only in c ++, how to rewrite it in js? - mobius
  • @mobius, replace int At var, and sizeof(v)/sizeof(v[0]) with length - Grundy
  • @pavel, can you explain what of the variables is what? What is "v", "l", "r"? - mobius

I give an example implementation on JS with comments:

 let array = prompt('Π’Π²Π΅Π΄ΠΈΡ‚Π΅ Ρ†ΠΈΡ„Ρ€Ρ‹ Ρ‡Π΅Ρ€Π΅Π· ΠΏΡ€ΠΎΠ±Π΅Π»:', '1 2 3 4 6 3 2 4 7 3 4 6 1 2 1').replace(/ +/g, ' ').trim().split(' '); // Π²Π²ΠΎΠ΄ΠΈΠΌ массив const height = Math.max(...array); // минимальная высота сСтки const width = array.length; // минимальная ΡˆΠΈΡ€ΠΈΠ½Π° сСтки const air = '–'; // символ Π²ΠΎΠ·Π΄ΡƒΡ…Π° const land = '='; // символ Π·Π΅ΠΌΠ»ΠΈ const water = 'β‰ˆ'; // символ Π²ΠΎΠ΄Ρ‹ const regExp = `(?${land}(${land}${air}+${land}))`; // рСгулярноС Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅, ΠΏΡ€ΠΈ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π²ΠΎΠ΄Π° упираСтся Π² зСмлю ΠΏΠΎ Π±ΠΎΠΊΠ°ΠΌ const grid = []; // ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ сСтку for (let i = height - 1; i >= 0; i--) { // ΠΏΡ€ΠΎΠ±Π΅Π³Π°Π΅ΠΌ ΠΏΠΎ рядам grid[i] = []; // ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ ряд for (let j = 0; j < width; j++) grid[i][j] = (array[j] > 0) ? land : air; // ΠΏΡ€ΠΎΠ±Π΅Π³Π°Π΅ΠΌ ΠΏΠΎ ΠΊΠΎΠ»ΠΎΠ½ΠΊΠ°ΠΌ let string = grid[i].join(''); // ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΠΎΠ²Ρ‹Π²Π°Π΅ΠΌ массив Π² строку Π±Π΅Π· запятых let samples = []; // ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ вхоТдСния string.replace(new RegExp(regExp, 'g'), (m, g) => samples.push(g)); // собираСм совпадСния Π² ΠΎΠ±Ρ‰ΠΈΠΉ массив (совпадСния ΠΌΠΎΠ³ΡƒΡ‚ Π½Π°ΠΊΠ»Π°Π΄Ρ‹Π²Π°Ρ‚ΡŒΡΡ Π΄Ρ€ΡƒΠ³ Π½Π° Π΄Ρ€ΡƒΠ³Π° Π² строкС) if (samples.length > 0) { // Ссли совпадСния ΠΏΠΎ рСгулярному Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΡŽ Π½Π°ΠΉΠ΄Π΅Π½Ρ‹, Ρ‚ΠΎ... samples.forEach((el) => { // для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ совпадСния... string = string.replace(el, el.replace(new RegExp(air, 'g'), water)); // мСняСм Π²ΠΎΠ΄Ρƒ Π½Π° Π²ΠΎΠ·Π΄ΡƒΡ… grid[i] = Array.from(string); // сохраняСм строку Π² Π²ΠΈΠ΄Π΅ массива }); } array = array.map((el) => (el > 0) ? --el : 0); // с ΠΊΠ°ΠΆΠ΄Ρ‹ΠΌ рядом ΡƒΠΌΠ΅Π½ΡŒΡˆΠ°Π΅ΠΌ Π½Π° 1 ΠΊΠ°ΠΆΠ΄ΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π² исходном массивС (Π½ΠΎ Π½Π΅ Π½ΠΈΠΆΠ΅ 0) } for (const el of grid) document.body.innerHTML += `<p>${el.join('').replace(new RegExp(land, 'g'), '<span class="land"> </span>').replace(new RegExp(air, 'g'), '<span class="air"> </span>').replace(new RegExp(water, 'g'), '<span class="water"> </span>')}</p>`; // отрисовываСм 
 p { font-family: monospace; /* Π΄Π΅Π»Π°Π΅ΠΌ символы Π² ΡˆΡ€ΠΈΡ„Ρ‚Π΅ ΠΎΠ΄ΠΈΠ½Π°ΠΊΠΎΠ²ΠΎΠ³ΠΎ Ρ€Π°Π·ΠΌΠ΅Ρ€Π° */ margin: 0; /* ΡƒΠ±ΠΈΡ€Π°Π΅ΠΌ отступы */ white-space: pre; /* позволяСм ΠΏΡ€ΠΎΠ±Π΅Π»Π°ΠΌ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Ρ‚ΡŒΡΡ */ } .air { background-color: lightblue; /* Ρ†Π²Π΅Ρ‚ Π²ΠΎΠ·Π΄ΡƒΡ…Π° */ } .land { background-color: green; /* Ρ†Π²Π΅Ρ‚ Π·Π΅ΠΌΠ»ΠΈ */ } .water { background-color: blue; /* Ρ†Π²Π΅Ρ‚ Π²ΠΎΠ΄Ρ‹ */ } 

PS Minecraft is still far away ... :)