最高の周辺ポリオミノ


14

これはコードゴルフです。勝者は、バイト数が最小の有効なコードです。


チャレンジ

入力MN、正方形の長方形グリッドの幅と高さを指定すると、次を満たすポリゴンを出力します。

  • 多角形のエッジは正方形のエッジのみで構成されています。斜めのエッジはありません。すべて垂直または水平です。
  • 多角形には穴がありません。多角形の外側のすべての正方形は、長方形の外側の境界にある多角形の外側の正方形から開始して、多角形の外側の正方形の直交ステップによって到達できます。
  • ポリゴンには自己交差がありません。頂点で交わる正方形のエッジのうち、ポリゴンの周囲の一部は2つまでです。
  • 多角形は接続されています。多角形内の正方形は、多角形内にとどまる直交ステップを介して、多角形の他の正方形から到達可能でなければなりません。
  • ポリゴンには、次の式に従って、可能な最大の境界線があります。

コードは、1〜255のMおよびNで機能する必要があります。


最大周囲長の式

ここでの課題は、最大の周囲長を持つこれらのポリゴンの中で最もゴルフに適したものを見つけることです。最大境界自体は、常に次の式で定義されます。

これは、最大の境界線ではすべての正方形の頂点が境界線上にある必要があるためです。奇数の頂点の場合、これは不可能であり、達成できる最適なものは頂点が1つ少ないことです(境界は常に偶数であるため)。


出力

改行で区切られた文字列(正確にM文字のN行)として形状を出力します。ここでは、多角形の外側の正方形にスペースを使用し、多角形の内側の正方形に「#」を使用していますが、すべての入力で意味が一貫していれば、視覚的に異なる2つの文字を使用できます。

最大1つの先行改行と最大1つの後続改行を含めることができます。

必要に応じて、代わりに正確にN文字のM行を出力し、一部の入力ではM × N出力を選択し、他の入力ではN × M出力を選択できます。


穴のため無効です:

###
# #
###

交差(斜めに触れる-周囲に4つの正方形のエッジを持つ頂点)と、偶然に穴があるため無効です:

##
# #
###

切断されているため無効です:

#
# #
  #

最大周囲の有効なポリゴン:

# #
# #
###

クレジット

私は当初、最大周囲長の値がどれくらい速く計算できるかを過小評価していましたが、その値を出力として求めるだけでした。任意のNとMの最大境界線を計算する方法を説明し、これを複数の回答が続く課題に変えるのを助けてくれた、チャットの非常に役立つ人々に感謝します...

特に感謝します:

スパーズガルブフェールサムジミー23013


ポリオミノまたはポリゴンを使用してこの質問に名前を付けることができます(両方が適用されるため)。誰か好みがありますか?コメント投票で以下を示すことができます
。– trichoplax

5
最高周囲ポリオミノ
-trichoplax

1
最も高い周囲の接続されたポリゴン
-trichoplax

正確にM文字のN行:特定の入力に便利だとわかった場合、2つの入力値を交換できますか?
レベルリバーセント

3
@steveverrill出力セクションを編集しました。それはあなたの要求に合っていますか?
-trichoplax

回答:


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.

結果には2つの主なケースがあります。少なくとも1つのサイズが奇数の場合、パターンは単純な「すくい」です。たとえば、入力の場合7 6

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

両方のサイズが均等である場合、1つおきの正方形が「オン」である追加の列があります。たとえば、入力の場合8 6

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

ここで、これらのパターンが問題の説明で与えられた境界の理論上の最大値に到達することを示すために、最初のパターンの境界(M + 1) * (N + 1)があり、2番目のパターンが同じ値から1を引いていることを確認する必要があります。

最初のパターンについては、境界についてM、奇数の寸法であります:

  1. M 上端に。
  2. 2 一番上の行の横に。
  3. (M - 1) / 2 歯の隙間に。
  4. (M + 1) / 22 * (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が偶数である2番目の場合、境界は次のように合計されます。

  1. M 上端に。
  2. 2 一番上の行の横に。
  3. M / 2 一番上の行の#を開きます。
  4. M / 22 * (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

Ruby、Rev 1、66

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

m0または1の累乗を使用して、1またはm #'sを印刷するかどうかを決定します。

>代わりに最後の行をテストするために使用され==ます。

プットやブラケットの後のスペースを取り除くことはできません!

Ruby、Rev 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)

最後に、MとNを交換できるかどうかを尋ねた後、私はそれを必要としませんでした。


Nが奇数の場合の一般的な出力。#右側で自分で削除すると、明らかに(N + 1)(M + 1)になります。図形を結合するためにそれらを含めると、水平方向の周囲の2つの正方形が削除され、垂直方向の周囲の2つの正方形が追加されるため、変更はありません。

ここでは、式"#"*(i%2==0?m:1)に基づいて、M #個のシンボルと1つの#シンボルの交互の行を与え、M 個の文字を右揃えにします。

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

N偶数の典型的な出力。5 6明確に、と同じ周囲長6 5、または5 5最下列のぎざぎざによる垂直周囲長の追加と比較して、M + 1 = 6の増分を持っています。6 6は、6 5垂直境界で(M + 1)-1 = 6の増分と同じです。したがって、それらは式に従っています。

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

Ruby rjustを使用すると、空のセルに使用するパディングを指定できるので非常に便利です。通常、パディングはに設定されますが" "、最後の行については切り替え"# "ます(Nが偶数の場合、最後の行にのみパディングが必要になります。Nが奇数の場合、最後の行は完了し、正当化されません。クレネレーションは表示されません。)

こちらをご覧ください。


@ Vioz-イデオンをありがとう!NとMの低い値までプログラムをテストして、エッジケースがあるかどうかを確認しましたが、それ以上の値で機能するかどうかは確認しませんでした。どうやら、銃創と複製の両方が正しいので、私はそれを残します。ブラケットと空白を削除できるかどうかを確認するために、後で戻ってきます。
レベルリバーセント

リンクに問題はありませんか?私はそれをテストするためにそれを使用したので、他の人に役立つだろうと思いました:P Rubyについてはあまり知りません(何も、事実ではありません)が、1バイトを保存するi%2==0ようi%2<1に変更できます(ideoneリンクにこの変更を加えました)。
カデ

#最後の行にもパディングが本当に必要ですか?たとえば、最後の図では#、右下隅なしで周囲は同じではありませんか?
レトコラディ

@RetoKoradiそれは確かに同じ境界線になります-コードが余分なものを含むように見えますが、#それは単にすべての行が終了する方法であるためです。(しかし、ルビーは知りません...)。
trichoplax

1
@trichoplaxあなたの直感は正しいです。パディングは、後者が奇数Mに隣接する2を与えるためでは"# "なく" #"、これは明らかに望ましくありません##Mで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))mod 2。ここで(N、M)で機能することを示します。

指を追加するプロセスにより、ポリゴンから1つのエッジが削除され、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)modです。 2。ここで(N、M)で機能することを示します。

手の高さを増やすと、最初と1つおきのxインデックスにある指が伸びるだけです。各高さの増加のために、各フィンガーは、周辺への2つを追加し、存在する(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に対して解が最適であることを証明します。

偶数のMの場合、奇数のMの場合と同じようにボードに入力しますが、最後のセグメントにクレネレーションを追加します。例えば:

4x3
# ##
# # 
####

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

この戦略が最適であることを証明しました。

偶数Mの帰納法:
解が(N、M-1)に対して正しいと仮定し、奇数M-1(最後のケースで証明されたように)で、(N + 1)M-( M(N + 1))mod 2。ここで、(N、M)で機能することを示します。

指を増やすように、各クレネレーションはポリゴンの周囲に2を追加します。複製の総数は(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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.