Найвищий периметр полиомино


14

Це код гольфу. Переможець - дійсний код з найменшою кількістю байтів.


Виклик

Давши входи M і N , ширину і висоту прямокутної сітки квадратів, вивести багатокутник, який задовольняє наступному:

  • Краї багатокутника складаються лише з квадратних ребер: діагональних ребер немає - всі вертикальні або горизонтальні.
  • У полігоні немає дірок: кожен квадрат поза полігоном може бути досягнутий ортогональними кроками на квадратах поза полігоном, починаючи з квадрата поза полігоном на зовнішній межі прямокутника.
  • Багатокутник не має самоперетину: з квадратних ребер, що зустрічаються у вершині, не більше 2 може бути частиною периметра многокутника.
  • Полігон з’єднаний: будь-який квадрат у полігоні повинен бути доступний до будь-якого іншого квадрата в полігоні через ортогональні сходи, що знаходяться в межах багатокутника.
  • Багатокутник має максимально можливий периметр: відповідно до наведеної нижче формули.

Ваш код повинен працювати для M і N від 1 до 255.


Формула для максимального периметра

Завдання тут - пошук найбільш гольфуючих із цих полігонів з максимальним периметром. Самий максимальний периметр завжди визначається формулою:

Це вірно, оскільки для максимального периметра кожна квадратна вершина повинна бути по периметру. Для непарної кількості вершин це неможливо, і найкраще, що можна досягти, - це одна вершина менше (оскільки периметр завжди парний).


Вихідні дані

Виведіть фігуру у вигляді рядка розділених символами нового рядка ( N рядків саме M символів). Тут я використовую простір для квадратів поза полігоном, а "#" - квадрати всередині полігону, але ви можете використовувати будь-які два візуально відмінних символи, за умови, що їх значення відповідає всім введенням.

Ви можете включати до однієї провідної нової та до однієї зворотної.

Якщо ви хочете, ви можете замість виведення M рядки рівно N символів, і ви можете вибрати M на N виходом для деяких входів і N по M виходом для інших.


Приклади

Недійсний через отвір:

###
# #
###

Недійсний через перехрестя (дотик по діагоналі - вершина з 4 квадратними краями по периметру) і, до речі, отвір:

##
# #
###

Недійсний через відключення:

#
# #
  #

Дійсний багатокутник максимального периметра:

# #
# #
###

Кредити

Я спочатку недооцінював, як швидко можна було обчислити значення максимального периметра, і збирався просто просити це значення як вихід. Завдяки чудово корисним людям у чаті за пояснення, як розробити максимальний периметр для довільних N та M та допомогти перетворити це на виклик, який триватиме не одну відповідь ...

Зокрема завдяки:

Sparr , Zgarb , feersum , jimmy23013 .


Я можу назвати це питання, використовуючи поліоміно чи полігони (оскільки застосовуються обидва). Хтось має перевагу? Ви можете коментувати голосування таким чином:
трихоплакс

5
Найвищий периметрій поліоміно
трихоплакс

1
Найвищий по периметру з’єднаний багатокутник
трихоплакс

N рядків з точно M символів: чи можемо ми замінити два вхідні значення, якщо нам здається, що це зручно для певних входів?
Рівень річки Св.

3
@steveverrill Я редагував розділ Вихідні дані. Чи відповідає це вашому запиту?
трихоплакс

Відповіді:


4

CJam, 47 байт

l~_2%{\}|_'#:H*@({N+1$(2md\HS+*H+\SH+R=*++}fR\;

Спробуйте в Інтернеті

Пояснення:

l~      Get and convert input.
_2%     Calculate second value modulo 2.
{\}|    If value is even, swap the two inputs. This puts odd on top if one is odd.
_'#:H*  Create top row of all # signs. Also save away # character as shortcut for later.
@(      Pull number of rows to top, and decrement because first is done.
{       Start loop over rows.
N+      Add newline.
1$      Copy row length to top of stack.
(2md    Decrement, and calculate mod/div with 2.
\       Swap mod and div, will use div first.
HS+     "# "
*       Repeat it based on div 2 of row length.
H+      Add one more #.
\       Swap mod of earlier division to top.
SH+     " #"
R=      Pick space or # depending on even/odd row number.
*       Repeat 0 or 1 times depending on mod 2 of row length.
+       Add the possible extra character to line.
+       Add line to result.
}fR     End of for loop over lines.
\;      Remove row length from stack, leaving only result string.

Є два основні випадки для результату. Якщо принаймні один з розмірів є непарним, візерунок - це звичайна «грабля». Наприклад, для введення 7 6:

#######
# # # #
# # # #
# # # #
# # # #
# # # #

Якщо обидва розміри рівні, є додатковий стовпчик, де кожен другий квадрат "включений". Наприклад, для введення 8 6:

########
# # # # 
# # # ##
# # # # 
# # # ##
# # # # 

Тепер, щоб показати, що ці закономірності досягають теоретичного максимуму периметра, як зазначено в описі проблеми, нам потрібно підтвердити, що перший візерунок має периметр (M + 1) * (N + 1), а другий - те саме значення мінус 1.

Для першого малюнка у нас є периметр з Mнепарним розміром:

  1. M для верхнього краю.
  2. 2 збоку верхнього ряду.
  3. (M - 1) / 2 для проміжків між зубами.
  4. (M + 1) / 2зуби з периметром 2 * (N - 1) + 1кожен.

Це додає до:

M + 2 + (M - 1) / 2 + (M + 1) / 2 * (2 * (N - 1) + 1) =
M + 2 + (M - 1) / 2 + (M + 1) * (N - 1) + (M + 1) / 2 =
2 * M + 2 + (M + 1) * (N - 1) =
(M + 1) * 2 + (M + 1) * (N - 1) =
(M + 1) * (N + 1)

Для другого випадку, коли обидва Mі Nрівні, периметр складається з:

  1. M для верхнього краю.
  2. 2 збоку верхнього ряду.
  3. M / 2 для відкритого # у верхньому ряду.
  4. M / 2зуби з периметром 2 * (N - 1) + 1кожен для простих зубів.
  5. Крайній правий зуб має додаткові 2 * (N / 2 - 1)шматочки по периметру для кульок.

Додайте це все разом:

M + 2 + M / 2 + (M / 2) * (2 * (N - 1) + 1) + 2 * (N / 2 - 1) =
M + 2 + (M / 2) * (2 * (N - 1) + 2) + N - 2 =
M + M * N + N =
(M + 1) * (N + 1) - 1

Я думаю, що можу зберегти пару байтів, розмістивши зубчасту частину зліва. Потрібно трохи менше перетасувати стек. Але час спати ...
Рето Коради

5

Рубі, Об. 1, 66

->(m,n){n.times{|i|puts ("#"*m**(1-i%2)).rjust(m,i>n-2?"# ":" ")}}

Використовується підйом mдо потужності 0 o 1, щоб визначити, чи m #буде надруковано 1 або s.

Використовується >для тестування останнього рядка замість ==.

Не вдається позбутися місця після розміщення, а також жодних дужок!

Рубі, Рев. 0, 69

->(m,n){n.times{|i|puts ("#"*(i%2==0?m:1)).rjust(m,i==n-1?"# ":" ")}}

Це анонімна лямбда-функція. Використовуйте його так:

f=->(m,n){n.times{|i|puts ("#"*(i%2==0?m:1)).rjust(m,i==n-1?"# ":" ")}}

M=gets.to_i
N=gets.to_i
f.call(M,N)

Зрештою, після запитання, чи можна міняти М і N, я цього не потребував.


Типові виходи для N непарних. Якщо ми видалимо #самостійно праворуч, явно матимемо (N + 1) (M + 1). Якщо включити їх до фігури, видаляється 2 квадрата горизонтального периметра і додається 2 квадрата вертикального периметра, так що змін не відбувається.

Тут ми покладаємось на вираз, "#"*(i%2==0?m:1)щоб дати чергування рядків з M #символів і одного #символу, а право виправдати M символів.

5                        6
5                        5
#####                    ######
    #                         #
#####                    ######
    #                         #
#####                    ######

Типові виходи для N парних. 5 6Очевидно має той самий периметр, як 6 5або приріст М + 1 = 6 порівняно з 5 5додаванням вертикального периметра за рахунок кренеції нижнього ряду. 6 6має таку ж 6 5величину, як плюс приріст (M + 1) -1 = 6 по вертикальному периметру. Таким чином, вони відповідають формулі.

5                        6
6                        6
#####                    ######
    #                         #
#####                    ######
    #                         #
#####                    ######
# # #                    # # ##

Дуже зручно, що Ruby's rjustдозволяє вказати прокладки для використання для порожніх комірок. Зазвичай для прокладки встановлено значення, " "але для останнього рядка ми переходимо до цього "# "(зауважте, що підкладка буде потрібна лише для останнього ряду, якщо N парне. Якщо N непарний, останній рядок буде повним і не буде обгрунтування, тож ви не побачить творінь.)

Перевірте це тут.


@ Vioz- Спасибі за ideone! Я перевірив програму до низьких значень N та M, щоб побачити, чи були випадки ребра, але я не намагався перевірити, чи буде вона працювати на такі великі значення. Мабуть, і кренеляція, і кренеляція є правильними, тому я залишу це. Повернусь пізніше, щоб побачити, чи можу я видалити деякі дужки та пробіли.
Рівень річки Св.

Немає проблем для посилання? Я вважав, що це буде корисно для інших, оскільки я використовував це для тестування: P Що стосується редакції правопису, я змінив його на перший результат, який я міг знайти, тому що я ніколи не бачив, щоб слово насправді не вживалося. Я не знаю багато про Рубі (нічого, Infact), але ви можете змінити , i%2==0щоб i%2<1зберегти байти (я зробив це зміна на заслання ideone).
Кейд

Вам справді потрібна #набивка для рівного останнього ряду? Наприклад, на останньому малюнку, чи не периметр однаковий без #нижнього правого кута?
Рето Коради

@RetoKoradi це дійсно був би той самий периметр - схоже, що код включає додаткові #просто тому, що це вже так, як закінчується кожен рядок, тому це менше байтів, ніж розміщення місця там. (Я не знаю, як рубін ...).
трихоплакс

1
@trichoplax ваша інтуїція правильна. Прокладка полягає "# "не в " #"тому, що остання дасть 2 сусідніх #для непарного М, що точно не хочеться. 2 сусідня #для навіть М не шкодить, тому я пішов з цим. Я не намагався ljust, можливо, можна зробити це більш чисто з цим, але це було б не так очевидно, що я друкую саме M символів на рядок.
Рівень річки Св.

5

C, 109 97 байт і підтвердження правильності

Я писав своє рішення, але @steveverrill переміг мене до цього. Я думав, що поділюсь би тим же, оскільки я включив доказ коректності для використовуваної стратегії.

Скорочений код:

m,n,x;main(){for(scanf("%i%i",&m,&n); n;)putchar(x<m?"# "[x%2*(++x^m||~n&1)&&n^1]:(x=0,n--,10));}

Перед зменшенням:

m,n,x;

main(){
    for(scanf("%i%i",&m,&n); n;) 

        /* If x == m, prints out a newline, and iterates outer 
         * loop (x=0,n--) using comma operator.
         * Otherwise, paints a '#' on :
         *     Every even column (when x%2 is 0)
         *     On odd columns of the last row (++x^m||~n&1 is 0)
         *     On the first row (when n^1 is 0)
         * And a ' ' on anything else (when predicate is 1) */
        putchar(x<m?"# "[x%2*(++x^m||~n&1)&&n^1]:(x=0,n--,10));
}

Стратегія та доказ:

Припускаючи правильність рівняння максимального периметра (M + 1) (N + 1) - ((M + 1) (N + 1)) mod 2 , далі пояснюється використана оптимальна стратегія та доводиться її правильність шляхом індукції:

Для непарного M ми малюємо рукоподібну форму з M / 2 + 1 пальцями, наприклад:

3x2
# # 
###

5x3
# # #
# # #
#####

Тепер ми доводимо, що ця стратегія є оптимальною для всіх непарних M шляхом індукції:

Базовий випадок: M = N = 1
Єдина комірка заповнена. Рішення правильне, оскільки (1 + 1) * (1 + 1) = 2 * 2 = 4, а квадрат має 4 сторони.

Індукція на ширину:
Припустимо, що стратегія форми руки працює для (N, M-2), де M непарне, тобто його перимітер є оптимальним і є (N + 1) (M - 2 + 1) + ((M -1) (N + 1)) мод 2 . Тепер ми покажемо, що це буде працювати для (N, M) .

Процес додавання пальця видаляє один край від багатокутника і додає 3 + 2N . Наприклад:

 5x3 -> 7x3
 # # # $
 # # # $
 #####$$

Поєднуючи це з нашою гіпотезою, що попередній периметр був оптимальним, новий периметр:

(N + 1)*(M - 2 + 1) - ((M+1)*(N+1)) mod 2 - 1 + 3 + 2*N
(N + 1)*(M + 1) - ((M-1)*(N+1)) mod 2 - 2(N + 1) - 1 + 3 + 2*N
(N + 1)*(M + 1) - ((M-1)*(N+1)) mod 2

Оскільки ми маємо справу з арифметикою модуля 2,

((M-1)*(N+1)) mod 2 = ((M+1)*(N+1)) mod 2

Таким чином, доведення, що збільшення ширини додаванням пальців призводить до оптимального периметра.

Індукція на висоту:
Припустимо, що стратегія ручної форми працює для (N-1, M) , де M непарне, тобто його периметр є оптимальним і N (M + 1) + ((M + 1) N) мод 2 . Тепер ми покажемо, що це буде працювати для (N, M) .

Збільшення висоти руки просто подовжує пальці, розташовані на першому та кожному іншому x-індексі. З кожним збільшенням висоти кожен палець додає по периметру два, і є (M + 1) / 2 пальці, таким чином, збільшення N призводить до збільшення на 2 (M + 1) / 2 = M + 1 у периметр.

Поєднуючи це з гіпотезою, ми маємо, що новий периметр:

N*(M + 1) + ((M+1)*N) mod 2 + M + 1
(N + 1)*(M + 1) + ((M+1)*N) mod 2

Модульна арифметика дозволяє нам спростити останній доданок, щоб ми отримали:

(N + 1)*(M + 1) + ((M+1)*(N+1)) mod 2

Доведення, що рішення є оптимальним для всіх N> 0 і непарних M> 0.

Для парних М ми заповнюємо дошку так само, як і для непарного М, але додаємо твори в останній сегмент, наприклад:

4x3
# ##
# # 
####

6x4
# # #
# # ##
# # #
######

Зараз ми доводимо, що ця стратегія є оптимальною.

Індукція для парного M:
Припустимо, що рішення є правильним для (N, M-1), з непарним M-1 (як це було доведено в останньому випадку), який має оптимальний периметр (N + 1) M - ( M (N + 1)) мод 2 . Тепер ми покажемо, що це буде працювати для (N, M).

Як і збільшення пальців, кожне кренелювання додає два по периметру багатокутника. Загальна кількість творень становить (N + N mod 2) / 2 , для загальної кількості периметрів N + N mod 2 .

Поєднуючи це з гіпотезою, ми маємо, що новий периметр:

(N + 1)*M - (M*(N+1)) mod 2 + N + N mod 2
(N + 1)*(M + 1) - (M*(N+1)) mod 2 + N mod 2 - 1
(N + 1)*(M + 1) - (M*(N+1)) mod 2 - (N + 1) mod 2

У нас це є

(M*(N+1)) mod 2 - (N + 1) mod 2 = ((M+1)*(N+1)) mod 2

Тому що якщо N непарне, то це зменшується до 0 = 0, а якщо N парне, воно зменшується до

- A mod 2 - 1 = -(A + 1) mod 2

Таким чином, стратегія є оптимальною для всіх M, N> 0 .


2
Це багато математики! Не могли ви просто обчислити периметр форми, яку ви створюєте, і показати, що вона відповідає заданому максимальному значенню? Ви знаєте, скільки у вас є «пальців», як довгий кожен палець тощо. Тому обчислити периметр слід досить просто.
Рето Коради

Правда. В деяких аспектах я вважаю, що шлях індукції є більш інтуїтивним, оскільки він є адитивним, але так, це призводить до більш тривалих пояснень.
Андре Хардер

Ви можете знати, що периметр дорівнює кількості цілих точок, які він проходить.
jimmy23013
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.