powered by simpleCommunicator - 2.0.49     © 2025 Programmizd 02
Форумы / Программирование [игнор отключен] [закрыт для гостей] / Задачка про остров
25 сообщений из 421, страница 7 из 17
Задачка про остров
    #39926686
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Портянка немного увеличилась, сделаны 2 изменения:

1) Теперь правильно вычисляется объем среза, когда он содержит сливающиеся ручейки.
Чтобы при этом не пострадала скорость, пришлось расширить структуру ячейки.

2) Изменены параметры функции CountCells, чтобы не повторять в ней ранее сделанные вычисления

Код: pascal
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.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
//Остров разбит на правильные шестиугольные ячейки площади 1 различной высоты над уровнем моря.
//Найти максимальный объем воды, которая не стечет в море после долгих проливных дождей.

type
  TCell= record                        //ячейка
    cx, cy, cz: integer;               //cx, cy - координаты, cz - высота ячейки
    next: integer;                     //следующая ячейка, куда течет вода, (если -1, то вытекает наружу)
    head: integer;                     //голова ручья (если -1, то это голова ручья, вытекающего наружу)
    end;

var
  c: array of TCell;                   //отсортированные по высоте ячейки
  ndx: array of array of integer;      //индекс для быстрого поиска ячейки в массиве c по паре координат (x,y)
  h: array[0..4, 0..4] of integer= (   //карта высот ячеек
    (0,1,5,5,0),
     (3,5,1,5,0),
    (6,1,2,4,6),
     (6,6,1,4,0),
    (0,3,4,4,0));

//сортирует массив c
procedure SortCells;
var
  i, j, k: integer;
  t: TCell;
begin;
  k:=Length(c)-1;
  if k<=0 then exit;
  j:=k; i:=j;
  repeat;
    dec(i);
    if c[i].cz>c[j].cz then j:=i;
    until i<=0;
  if j<>k then begin;
    t:=c[j]; c[j]:=c[k]; c[k]:=t;
    end;

  i:=k-1;
  while i>0 do begin;
    j:=i; dec(i);
    if c[i].cz>c[j].cz then begin;
      repeat;
        inc(j);
        until not (c[i].cz>c[j].cz);
      dec(j);
      t:=c[i];
      for k:=i to j-1 do c[k]:=c[k+1];
      c[j]:=t;
      end;
    end;
  end;

//читает карту высот, вызывает SortCells, формирует ndx
procedure ReadCells;
var
  x, y, xcount, ycount, i: integer;
begin;
  ycount:=Length(h);
  xcount:=Length(h[0]);
  SetLength(c, ycount*xcount);
  for y:=0 to ycount-1 do for x:=0 to xcount-1 do with c[y*xcount + x] do begin;
    cx:=x; cy:=y; cz:=h[y,x];
    end;
  SortCells;
  SetLength(ndx, ycount, xcount);
  for i:=0 to Length(c)-1 do ndx[c[i].cy, c[i].cx]:=i;
  end;

//для i-той ячейки возвращает позицию в массиве c ее j-того соседа, или -1
function Neighbour(i, j: integer): integer;
var
  x, y, d: integer;
begin;
  Result:=-1;
  x:=c[i].cx;
  y:=c[i].cy;
  d:=0; if y and 1=0 then d:=-1;
  case j of
    0: begin; x:=x+d; y:=y-1; end;
    1: begin; x:=x+d+1; y:=y-1; end;
    2: x:=x-1;
    3: x:=x+1;
    4: begin; x:=x+d; y:=y+1; end;
    5: begin; x:=x+d+1; y:=y+1; end;
    else begin; x:=-1; y:=-1; end;
    end;
  if (y>=0) and (y<Length(ndx)) and (x>=0) and (x<Length(ndx[0])) then Result:=ndx[y,x];
  end;

//считает ячейки в интервале [0..last] из которых не течет до высоты ячейки с[last+1]
function CountCells(first, last: integer): integer;
var
  i, j, n, k, sum: integer;
begin;
  Result:=0;
  //вычисляем значения next для ячеек c[first..last]
  for i:=first to last do begin;
    c[i].next:=i;
    for j:=0 to 5 do begin;
      n:=Neighbour(i,j);
      if n<0 then c[i].next:=-1
      else if n<i then begin;
        repeat;
          k:=n; n:=c[n].next;
          until (k=n) or (n<0);
        if n<0 then c[i].next:=-1 else c[n].next:=i;
        end;
      end;
    end;
  //инициализация перед подсчетом - каждый сам себе голова
  for i:=0 to last do c[i].head:=i;
  //считаем количество ячеек в каждом ручье, с учетом того, что они могут сливаться
  for i:=0 to last do begin;
    sum:=0; n:=i;
    repeat;
      k:=c[n].head;
      if k<>n then begin; //если у очередной ячейки определена голова, то это слияние ручьев
        if c[k].head<0 then n:=-1; //если из головы течет наружу, то и это ручей течет туда же
        break;
        end;
      c[n].head:=i; inc(sum); //запомнили голову ручья и ячейка пошла на ум
      k:=n; n:=c[n].next;
      until (k=n) or (n<0);
    if n<0 then c[i].head:=-1 else Result:=Result+sum; //если не вытекло, то прибавим, что в уме
    end;
  end;

//считает общий объем воды, как сумму объемов в каждом срезе
function FillCells: integer;
var
  i, j, dz: integer;
begin;
  Result:=0;
  ReadCells;
  j:=0;
  for i:=0 to Length(c)-2 do begin;
    dz:=c[i+1].cz - c[i].cz; //высота очередного среза
    if dz>0 then begin;
      Result:=Result + dz * CountCells(j,i); //добавили объем среза [j..i]
      j:=i+1;
      end;
    end;
  end;

procedure TForm1.Button1Click(Sender: TObject);
begin;
  Memo1.Lines.Add(IntToStr(FillCells));
  end;

...
Рейтинг: 0 / 0
Задачка про остров
    #39926715
Фотография Имя пользователя1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
закодю свой вариант, как время будет)
...
Рейтинг: 0 / 0
Задачка про остров
    #39926807
АСУ ТПшник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ой ой ой.
ТЗ со входными и выходными данными отсутствует. Предлагаю взять картинку Мэйтона и как-то визуализировать результаты. А то получается как всегда. Классы и мы, тупые АСУТПшники с геттерами сеттреами можем.
...
Рейтинг: 0 / 0
Задачка про остров
    #39926827
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Рекурсивный вариант получился немного короче.
Здесь процедуры ReadCells и Neighbour те же, что и в нерекурсивном варианте 22079993 .

Код: pascal
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.
procedure SetLevel(i, level: integer);
var
  j, n: integer;
begin;
  for j:=0 to 5 do begin;
    n:=Neighbour(i,j);
    if (n<0) or (c[n].head<=level) then continue;
    if c[n].cz>level then c[n].next:=n
    else begin;
      c[n].head:=level;
      SetLevel(n, level);
      end;
    end;
  end;

function FillCells2: integer;
var
  i, x, y, z: integer;
begin;
  Result:=0;
  ReadCells;
  x:=Length(h[0])-1;
  y:=Length(h)-1;
  z:=c[Length(c)-1].cz;
  for i:=0 to Length(c)-1 do begin;
    c[i].next:=-1;
    c[i].head:=z;
    end;
  for i:=0 to Length(c)-1 do with c[i] do
    if (cx=0) or (cx=x) or (cy=0) or (cy=y) or (next=i) then SetLevel(i, c[i].cz);
  for i:=0 to Length(c)-1 do with c[i] do
    if head>cx then Result:=Result + (head-cx);
  end;

procedure TForm1.Button2Click(Sender: TObject);
begin;
  Memo1.Lines.Add(IntToStr(FillCells));
  end;

...
Рейтинг: 0 / 0
Задачка про остров
    #39926830
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
АСУ ТПшник
Ой ой ой.
ТЗ со входными и выходными данными отсутствует. Предлагаю взять картинку Мэйтона и как-то визуализировать результаты. А то получается как всегда. Классы и мы, тупые АСУТПшники с геттерами сеттреами можем.

Предлагаю закрасить синим цветом где океан.
Ну и картинку - побольше. На весь экран.
...
Рейтинг: 0 / 0
Задачка про остров
    #39926835
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Предлагаю закрасить синим цветом где океан.
Ну и картинку - побольше. На весь экран.


Океан - ненужная деталь в этой задаче.
Есть квадратный остров, за его пределами - минус бесконечность.
Результат будет тем же.
...
Рейтинг: 0 / 0
Задачка про остров
    #39926836
iOracleDev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Можно взять и нарисовать картинку такого плана 22077903
как и предлагал mayton пиксели-квадраты с четырьмя сторонами, 256 градаций серого,
0-океан, 1-255 разные высоты чем темнее тем выше, хотя можно и инвертировать, кому как нравится,
перегнать ее в массив и обработать алгоритмом закрасив в 0 плитки оставшиеся под водой,
потом обратно в картинку и сравнивать что получилось.
...
Рейтинг: 0 / 0
Задачка про остров
    #39926843
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если лень формировать графику - воспользуйтесь псевдо-текстовым форматом (PPM) графики.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927062
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Мимо проходил, вопрос возник.
Соседство такое:
- 4 5 это типа м-ца для сотовых ячеек?
2 (ху) 3
- 0 1
Никто не пробовал в полярных координатах мыслить? мне показалось соседство натуральнее будет, а то в матрице вроде прыгает туда-сюда, хотя бы уж по кругу пустили как в полярных.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927063
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
exp98
Мимо проходил, вопрос возник.
Соседство такое:
- 4 5 это типа м-ца для сотовых ячеек?
2 (ху) 3
- 0 1
Никто не пробовал в полярных координатах мыслить? мне показалось соседство натуральнее будет, а то в матрице вроде прыгает туда-сюда, хотя бы уж по кругу пустили как в полярных.

Я иногда. Рассеяно разглядывая глобус думаю что полярная сетка координат - хреновая идея.
Вот если взять икосаэдр. И натянуть его на глобус. А потом образующие его треугольники разбить
еще на 4 треугольника. И т.д рекурсивно. То мы получаем вполне себе красивую систему координат
икосаэдральной системы. Где нет дефекта искажения координат как на полюсах. Равномерно
по всей поверхности треугольнички будут примерно равны. Хотя где-то в узловых точках искосаэдра
где сходятся в 1 точке 5 треугольников они будут не совсем равносторонние. Но это пустяк ИМХО.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927066
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В задаче как раз "нет дефекта искажения координат", всё на плоскости. Соседи только по окружности ( а это номер угла) и по радиусу (это вообще +-1). Ещё по прямым (квазикасательные к окружностям == секущие их в 2-х точках). Вот и все 6 направлений. Всё очень естественно. Лежит в той же м-це только по кругу, но я не навязываю. Может автор матрицы так и мыслил, из матрице только не скажешь.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927083
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Остров мог быть не шестиугольный. Могла быть система островов с изломанной береговой линией.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927091
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov
Рекурсивный вариант получился немного короче.
Здесь процедуры ReadCells и Neighbour те же, что и в нерекурсивном варианте 22079993 .

Код: pascal
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.
procedure SetLevel(i, level: integer);
var
  j, n: integer;
begin;
  for j:=0 to 5 do begin;
    n:=Neighbour(i,j);
    if (n<0) or (c[n].head<=level) then continue;
    if c[n].cz>level then c[n].next:=n
    else begin;
      c[n].head:=level;
      SetLevel(n, level);
      end;
    end;
  end;

function FillCells2: integer;
var
  i, x, y, z: integer;
begin;
  Result:=0;
  ReadCells;
  x:=Length(h[0])-1;
  y:=Length(h)-1;
  z:=c[Length(c)-1].cz;
  for i:=0 to Length(c)-1 do begin;
    c[i].next:=-1;
    c[i].head:=z;
    end;
  for i:=0 to Length(c)-1 do with c[i] do
    if (cx=0) or (cx=x) or (cy=0) or (cy=y) or (next=i) then SetLevel(i, c[i].cz);
  for i:=0 to Length(c)-1 do with c[i] do
    if head>cx then Result:=Result + (head-cx);
  end;

procedure TForm1.Button2Click(Sender: TObject);
begin;
  Memo1.Lines.Add(IntToStr(FillCells));
  end;



Давайте поставим более широко задачу. Допустим наш остров имеет очень мелкую и точную детализацию высот.
Даже не 1000х1000 пикселов а давайте зададим в 1млн на 1 млн.

Вобщем давайте подумаем о параллелизме основного алгоритма.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927094
АСУ ТПшник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
авторЕсть квадратный остров, за его пределами - минус бесконечность.
Как бы абсолютно согласен, но есть просьба, без кода на словах. Потому как концептуально обсуждаем все же. Никаких утилитарных применений не предвидится.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927096
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Давайте поставим более широко задачу. Допустим наш остров имеет очень мелкую и точную детализацию высот.
Даже не 1000х1000 пикселов а давайте зададим в 1млн на 1 млн.

Вобщем давайте подумаем о параллелизме основного алгоритма.


Зачем? Теоретически высота одного угла может влиять на решение в другом.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927120
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Параллелизм на графовых задачах - это сложная тема. Это - челендж.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927134
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
АСУ ТПшник, если на пальцах, то:

Рекурсивный алгоритм 22080232 работает со "срезающими" ячейками.
Так мы назовем те ячейки, которые срезают уровень воды в соседних ячейках
до своей высоты или своего уровня воды (до того, что выше).
После всех возможный срезаний сложим объем воды в каждой ячейке и получим ответ.

Чтобы уменьшить объем вычислений и обезопаситься от зацикливания,
мы предварительно отсортируем ячейки по высоте.
Сначала объявим срезающими все граничные ячейки и ячейку максимальной высоты.
Множество срезающих ячеек может пополняться в ходе вычислений.

Основной цикл перебирает ячейки в отсортированном массиве
и для каждой срезающей ячейки вызывает процедуру среза,
которая, перебирая ее соседей, делает следующее:
1) если уровень воды в соседе ниже или равен срезу, то пропускаем соседа,
2) если высота соседа выше среза, то объявляем соседа срезающей ячейкой,
3) иначе срезаем уровень воды в соседе и для него вызываем процедуру среза.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927176
iOracleDev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov,

Соседних? Замкнутые контуры он как отработает?
...
Рейтинг: 0 / 0
Задачка про остров
    #39927179
iOracleDev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Все таки самый правильный вариант идти сверху срезая по слоям, и разделяя ячейки на сухие и затопленные,
начинаем с максимальной высоты острова (если начинать не с максимальной, то замкнутые контуры на первом шаге
будут сухими), ВСЕ площадки не имеющие статуса и равные текущему срезу объявляются сухими сразу,
далее мы должны найти все замкнутые контуры на срезе равные по высоте срезу, ячейки внутри контуров,
которым не присвоен статус сухие, получают статус затопленные. Опускаемся на уровень ниже и
проделываем ту же процедуру, ячейки со статусом затопленные в рассмотрение не принимаются,
ячейки со статусом сухие могут образовывать контур, статусы установленные на предыдущих уровнях
изменению не подлежат, они окончательные. Вопрос только в том как искать замкнутые контуры на
уровнях, т.е. контуры у которых высота равна высоте среза или они уже имеют статус сухие.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927182
iOracleDev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Проходим по текущему срезу по периметру и ставим временный статус под водой на данном этапе отлива,
всем ячейкам до которых сможем добраться проходя от периметра вглубь, ячейки с высотой равной срезу
не переходим, таким образом у нас будут три градации на уровне, ячейки у которых ранее поставлен статус
сухие и ячейки равные по высоте срезу - сухие окончательно и бесповоротно, ячейки до которых смогли
добраться от периметра на данном срезе - временный статус под водой на данном этапе, на следующих
шагах этот статус не учитывается, все оставшиеся без статуса ячейки это ячейки внутри контуров и ниже
высоты среза, присваиваем им окончательный статус - под водой.

Нужен хороший алгоритм обхода от периметра в глубину всех связанных ячеек имеющих уровень ниже уровня
среза.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927186
iOracleDev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Поиск в ширину для каждого среза, для каждой точки периметра которая не имеет статус сухая и
не была найдена в поиске для предыдущих точек, перед обработкой следующего среза временный
статус под водой сбрасывается.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927192
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Здесь под спойлером исправленный рекурсивный алгоритм, он заметно быстрее нерекурсивного.

Оба алгоритма выдают одинаковый результат на больших массивах случайных данных,
что, по-видимому, означает, что в них одинаковое количество одинаковых ошибок )

Для больших массивов имеет смысл переписать процедуру SortCells,
заменив сортировку вставками на быструю сортировку.

Код: pascal
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.
procedure SetLevel(i, level: integer);
var
  j, n: integer;
begin;
  for j:=0 to 5 do begin;
    n:=Neighbour(i,j);
    if n>=0 then with c[n] do if head>=level then begin;
      if cz>level then begin;
        head:=cz;
        next:=n;
        end
      else if head>level then begin;
        head:=level;
        SetLevel(n, level);
        end;
      end;
    end;
  end;

function FillCells2: integer;
var
  i, x, y, z: integer;
begin;
  Result:=0;
  ReadCells;
  x:=Length(h[0])-1;
  y:=Length(h)-1;
  z:=c[Length(c)-1].cz;
  for i:=0 to Length(c)-1 do with c[i] do begin;
    if (cx=0) or (cx=x) or (cy=0) or (cy=y) then begin;
      next:=i;
      head:=cz;
      end
    else begin;
      next:=-1;
      head:=z;
      end;
    end;
  for i:=0 to Length(c)-1 do with c[i] do if next=i then SetLevel(i, c[i].cz);
  for i:=0 to Length(c)-1 do with c[i] do Result:=Result + (head-cz);
  end;

...
Рейтинг: 0 / 0
Задачка про остров
    #39927196
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
iOracleDev
Соседних? Замкнутые контуры он как отработает?


Все ячейки по границе контура станут срезающими и срежут уровень внутри контура.

iOracleDev
Все таки самый правильный вариант идти сверху срезая по слоям.


Это приведет к лишней работе по многократному срезу уровня в ячейках.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927204
exp98
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Остров мог быть не шестиугольный.
Это неважно, главное, что ячейки 6-ные.
...
Рейтинг: 0 / 0
Задачка про остров
    #39927219
iOracleDev
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov
Все ячейки по границе контура станут срезающими и срежут уровень внутри контура.

Внутри контура не обязательно все ниже, там могут быть дургие контуры, а в них еще контуры, не совсем понимаю каким образном наружные срежут все внутри, глубина лужи может быть маленькой, а лужа при этом очень высоко.

iOracleDev
Это приведет к лишней работе по многократному срезу уровня в ячейках.

Мы не будем рассматривать ячейки статус которых уже точно определен, по крайней мере описанный алгоритм логически понятен и даст верный результат, алгоритм с сортировкой по высоте меня сразу настораживает, каким образом он обработает многократно вложенные замкнутые контуры?
...
Рейтинг: 0 / 0
25 сообщений из 421, страница 7 из 17
Форумы / Программирование [игнор отключен] [закрыт для гостей] / Задачка про остров
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]