Polimino più alto perimetrale


14

Questo è il codice golf. Il vincitore è il codice valido con il minor numero di byte.


Sfida

Dati gli input M e N , la larghezza e l'altezza di una griglia rettangolare di quadrati, genera un poligono che soddisfa quanto segue:

  • I bordi poligonali sono costituiti solo da bordi quadrati: non ci sono bordi diagonali, tutti sono verticali o orizzontali.
  • Il poligono non ha buchi: ogni quadrato esterno al poligono può essere raggiunto da gradini ortogonali su quadrati esterni al poligono, a partire da un quadrato esterno al poligono sul bordo esterno del rettangolo.
  • Il poligono non ha autointersezione: dei bordi quadrati che si incontrano in corrispondenza di un vertice, non più di 2 possono far parte del perimetro del poligono.
  • Il poligono è collegato: qualsiasi quadrato nel poligono deve essere raggiungibile da qualsiasi altro quadrato nel poligono tramite passaggi ortogonali che rimangono all'interno del poligono.
  • Il poligono ha il perimetro massimo possibile: secondo la formula mostrata di seguito.

Il codice deve funzionare per M e N da 1 a 255.


Formula per il perimetro massimo

La sfida qui è trovare il più giocabile di quei poligoni con il perimetro massimo. Il perimetro massimo stesso è sempre definito dalla formula:

Questo è vero perché per un perimetro massimo ogni vertice quadrato deve trovarsi sul perimetro. Per un numero dispari di vertici questo non è possibile e il meglio che si può ottenere è un vertice in meno (poiché il perimetro è sempre pari).


Produzione

Stampa la forma come una stringa di caratteri separati da nuova riga ( N righe di esattamente M caratteri). Qui sto usando lo spazio per i quadrati all'esterno del poligono e "#" per i quadrati all'interno del poligono, ma è possibile utilizzare due caratteri visivamente distinti, a condizione che il loro significato sia coerente per tutti gli input.

È possibile includere fino a una nuova riga iniziale e fino a una nuova riga finale.

Se lo desideri, puoi invece generare M righe di esattamente N caratteri e puoi scegliere M per N output per alcuni input e N per M output per altri.


Esempi

Non valido a causa di un buco:

###
# #
###

Non valido a causa dell'intersezione (toccando in diagonale - un vertice con 4 bordi quadrati sul perimetro) e, per inciso, un foro:

##
# #
###

Non valido a causa della disconnessione:

#
# #
  #

Poligono valido del perimetro massimo:

# #
# #
###

Titoli di coda

Inizialmente ho sottovalutato la rapidità con cui è stato possibile calcolare il valore del perimetro massimo e avrei semplicemente chiesto quel valore come output. Grazie alle persone meravigliosamente disponibili in chat per aver spiegato come elaborare il perimetro massimo per N e M arbitrari e aver contribuito a trasformare questo in una sfida che durerà per più di una risposta ...

In particolare grazie a:

Sparr , Zgarb , feersum , jimmy23013 .


Potrei nominare questa domanda usando polyominos o poligoni (poiché entrambi si applicano). Qualcuno ha una preferenza? Puoi indicare con voto di commento quanto segue:
trichoplax,

5
Polimino più alto perimetrale
tricoplax,

1
Poligono più alto perimetrale collegato
trichoplax,

N righe di esattamente M caratteri: possiamo scambiare i due valori di input se lo troviamo conveniente per determinati input?
Level River St

3
@steveverrill Ho modificato la sezione Output. Soddisfa la tua richiesta?
trichoplax,

Risposte:


4

CJam, 47 byte

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

Provalo online

Spiegazione:

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.

Esistono due casi principali per il risultato. Se almeno una delle dimensioni è dispari, il motivo è un semplice "rastrello". Ad esempio, per l'input 7 6:

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

Se entrambe le dimensioni sono pari, è presente una colonna aggiuntiva in cui ogni secondo quadrato è "attivo". Ad esempio, per l'input 8 6:

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

Ora, per mostrare che questi schemi raggiungono il massimo teorico del perimetro come indicato nella descrizione del problema, dobbiamo confermare che il primo schema ha un perimetro (M + 1) * (N + 1)e il secondo lo stesso valore meno 1.

Per il primo modello, abbiamo per il perimetro, con Muna dimensione dispari:

  1. M per il bordo superiore.
  2. 2 sul lato della fila superiore.
  3. (M - 1) / 2 per gli spazi tra i denti.
  4. (M + 1) / 2denti con perimetro 2 * (N - 1) + 1ciascuno.

Questo si aggiunge a:

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)

Per il secondo caso in cui entrambi Me Nsono pari, il perimetro si somma da:

  1. M per il bordo superiore.
  2. 2 sul lato della fila superiore.
  3. M / 2 per il # aperto nella riga superiore.
  4. M / 2denti con perimetro 2 * (N - 1) + 1ciascuno per i denti normali.
  5. Il dente più a destra ha un 2 * (N / 2 - 1)pezzo extra perimetrale per le jaggies.

Aggiungendo tutto questo insieme:

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

Penso di poter salvare un paio di byte posizionando la parte frastagliata a sinistra. Dovrebbe essere necessario un po 'meno di mescolamento dello stack. Ma è ora di dormire ...
Reto Koradi,

5

Ruby, Rev 1, 66

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

Usato per aumentare mdi potenza 0 o 1 per decidere se 1 o m #'verranno stampati.

Utilizzato >per testare l'ultima riga anziché ==.

Non riesco a liberarmi dello spazio dopo le put, né eventuali parentesi!

Ruby, Rev 0, 69

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

Questa è una funzione lambda anonima. Usalo in questo modo:

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)

Alla fine, dopo aver chiesto se M e N potessero essere scambiati, non ne avevo bisogno.


Uscite tipiche per N dispari. Se cancelliamo #da soli sul lato destro, chiaramente avremo (N + 1) (M + 1). Includerli per unire la forma rimuove 2 quadrati di perimetro orizzontale e aggiunge 2 quadrati di perimetro verticale, quindi non ci sono cambiamenti.

Qui facciamo affidamento sull'espressione "#"*(i%2==0?m:1)per fornire righe alternate di #simboli M e un #simbolo, e giustamente giustificano i caratteri M.

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

Uscite tipiche per N pari. 5 6ha chiaramente lo stesso perimetro 6 5o un incremento di M + 1 = 6 rispetto 5 5all'aggiunta del perimetro verticale a causa della merlatura della fila inferiore. 6 6ha lo stesso valore 6 5più un incremento di (M + 1) -1 = 6 nel perimetro verticale. Pertanto sono conformi alla formula.

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

È molto utile che Ruby rjustti consenta di specificare l'imbottitura da utilizzare per le celle vuote. Normalmente il padding è impostato su " "ma per l'ultima riga passiamo a "# "(nota che il padding sarà necessario solo sull'ultima riga se N è pari. Dove N è dispari, l'ultima riga sarà completa e non ci saranno giustificazioni, quindi tu non vedrà le merlature.)

Controllalo qui.


@Vioz- Grazie per l'ideone! Ho testato il programma fino a valori bassi di N e M per vedere se c'erano casi limite, ma non mi sono preoccupato di verificare se avrebbe funzionato per valori così alti. Apparentemente sia la merlatura che la merlatura sono corrette, quindi la lascio. Torneremo più tardi per vedere se posso eliminare alcune parentesi e spazi bianchi.
Level River St

Nessun problema per il link? Ho pensato che sarebbe stato utile per gli altri da quando l'ho usato per testare: P Per quanto riguarda la modifica dell'ortografia, l'ho cambiata al primo risultato che ho trovato, perché non ho mai visto la parola effettivamente utilizzata. Non so molto di Ruby (niente, infatti), ma puoi cambiare i%2==0in i%2<1per salvare un byte (ho apportato questa modifica al collegamento ideone).
Kade,

Hai davvero bisogno #dell'imbottitura per l'ultima riga? Ad esempio, nell'ultima figura, il perimetro non è lo stesso senza l' #angolo in basso a destra?
Reto Koradi,

@RetoKoradi sarebbe davvero lo stesso perimetro - sembra che il codice includa l'ulteriore #semplicemente perché è già il modo in cui ogni linea è terminata, quindi è meno byte che inserire uno spazio lì. (Non conosco il rubino però ...).
trichoplax,

1
@trichoplax la tua intuizione è corretta. L'imbottitura "# "non è " #"perché quest'ultima darebbe 2 adiacenti #per la M dispari che sicuramente non è desiderata. 2 adiacente #anche a M non fa male, quindi ci sono andato. Non ho provato ljust, potrebbe essere possibile farlo in modo più pulito con quello, ma non sarebbe così ovvio che sto stampando esattamente M caratteri per riga.
Level River St,

5

C, 109 97 byte e prova di correttezza

Stavo scrivendo la mia soluzione ma @steveverrill mi ha battuto. Ho pensato di condividerlo lo stesso, dal momento che ho incluso una prova di correttezza per la strategia utilizzata.

Codice ridotto:

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

Prima della riduzione:

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

Strategia e prove:

Supponendo la correttezza dell'equazione perimetrale massima (M + 1) (N + 1) - ((M + 1) (N + 1)) mod 2 , il seguente spiega la strategia ottimale utilizzata e ne dimostra la correttezza per induzione:

Per la M dispari, disegniamo una forma simile a una mano con M / 2 + 1 dita, ad esempio:

3x2
# # 
###

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

Ora dimostriamo che questa strategia è ottimale per tutte le M dispari per induzione:

Caso base: M = N = 1
La singola cella è piena. La soluzione è corretta poiché (1 + 1) * (1 + 1) = 2 * 2 = 4 e un quadrato ha 4 lati.

Induzione sulla larghezza:
supponi che la strategia della forma della mano funzioni per (N, M-2) dove M è dispari, cioè il suo perimetro è ottimale ed è (N + 1) (M - 2 + 1) + ((M -1) (N + 1)) mod 2 . Ora mostriamo che funzionerà per (N, M) .

Il processo di aggiunta di un dito rimuove un bordo dal poligono e aggiunge 3 + 2N . Per esempio:

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

Combinando questo con la nostra ipotesi che il perimetro precedente fosse ottimale, il nuovo perimetro è:

(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

Dato che abbiamo a che fare con l'aritmetica modulo 2,

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

Pertanto, dimostrando che aumentando la larghezza aggiungendo le dita si ottiene un perimetro ottimale.

Induzione in altezza:
supponiamo che la strategia della forma della mano funzioni per (N-1, M) , dove M è dispari, cioè il suo perimetro è ottimale ed è N (M + 1) + ((M + 1) N) mod 2 . Ora mostriamo che funzionerà per (N, M) .

Aumentare l'altezza della mano allunga semplicemente le dita, situate al primo e ad ogni altro indice x. Per ogni aumento di altezza, ogni dito aggiunge due al perimetro e ci sono (M + 1) / 2 dita, quindi un aumento di N porta ad un aumento di 2 (M + 1) / 2 = M + 1 nella perimetro.

Combinando questo con l'ipotesi, abbiamo che il nuovo perimetro è:

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

L'aritmetica modulare ci consente di semplificare l'ultimo termine, in modo da ottenere:

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

Dimostrando che la soluzione è ottimale per tutti N> 0 e M dispari> 0.

Per M pari, riempiamo il tabellone come faremmo per M dispari, ma aggiungiamo merlature all'ultimo segmento, ad esempio:

4x3
# ##
# # 
####

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

Ora dimostriamo che questa strategia è ottimale.

Induzione per M pari:
supponiamo che la soluzione sia corretta per (N, M-1), con M-1 dispari (come è stato dimostrato nell'ultimo caso), che ha un perimetro ottimale di (N + 1) M - ( M (N + 1)) mod 2 . Ora mostriamo che funzionerà per (N, M).

Come aumentare le dita, ogni crenelation ne aggiunge due al perimetro del poligono. Il numero totale di merlature è (N + N mod 2) / 2 , per un totale di N + N mod 2 perimetrale aggiunto.

Combinando questo con l'ipotesi, abbiamo che il nuovo perimetro è:

(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

Abbiamo quello

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

Perché se N è dispari, allora questo si riduce a 0 = 0 e se N è pari, si riduce a

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

Pertanto la strategia è ottimale per tutti M, N> 0 .


2
È un sacco di matematica! Non potresti semplicemente calcolare il perimetro della forma che stai creando e mostrare che corrisponde al valore massimo fornito? Sai quante "dita" hai, quanto è lungo ogni dito, ecc. Quindi calcolare il perimetro dovrebbe essere ragionevolmente facile.
Reto Koradi,

Vero. Per alcuni aspetti, ritengo che il percorso di induzione sia più intuitivo, poiché è additivo, ma sì, porta a una spiegazione più lunga.
André Harder,

Potresti voler sapere che il perimetro è uguale al numero di punti interi che passa.
jimmy23013,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.