最高周长的多米诺骨牌


14

这是代码高尔夫。获胜者是字节数最少的有效代码。


挑战

给定输入MN,即正方形矩形网格的宽度和高度,输出满足以下条件的多边形:

  • 多边形边缘仅由正方形边缘组成:没有对角线边缘-全部为垂直或水平。
  • 多边形没有孔:从多边形外部边界上的多边形开始,可以在多边形外部的正方形上通过正交步骤到达多边形外部的每个正方形。
  • 多边形没有自相交:在一个顶点相交的正方形边缘中,多边形周长的一部分不得超过2个。
  • 多边形已连接:多边形中的任何正方形都必须可以通过停留在多边形内的正交步距多边形中的任何其他正方形。
  • 多边形具有最大可能的周长:根据下面显示的公式。

您的代码必须适用于从1到255的MN。


最大周长的公式

这里的挑战是要找到具有最大周长的那些多边形中最可打的高尔夫。最大周长本身始终由以下公式定义:

这是正确的,因为对于最大周长,每个正方形顶点必须在该周长上。对于奇数个顶点,这是不可能的,而可获得的最佳顶点数少一个顶点(因为周长始终为偶数)。


输出量

将形状输出为由换行符分隔的字符串(N行,正好是M个字符)。在这里,我为多边形外部的正方形使用空格,为多边形内部的正方形使用'#',但是您可以使用任何两个视觉上不同的字符,只要它们的含义对于所有输入都是一致的。

您最多可以包含一个前导换行符和最多一个尾随换行符。

如果愿意,您可以输出M个正好N个字符的行,对于某些输入,您可以选择M by N输出,而对于其他输入则选择N by M输出。


例子

由于孔无效

###
# #
###

由于交集(对角线触摸-顶点在外围具有4个正方形边缘)和一个孔而无效

##
# #
###

由于断开连接而无效

#
# #
  #

最大周长的有效多边形:

# #
# #
###

学分

最初,我低估了可以计算出最大周长的值的速度,并只是要求将该值作为输出。感谢聊天中非常有帮助的人们,他们解释了如何计算任意N和M的最大周长,并帮助将其变成一项挑战,将持续多个答案...

特别要感谢:

斯帕尔兹加布费瑟姆吉米23013


我可以使用多米诺骨牌或多边形来命名这个问题(因为两者都适用)。有人有偏好吗?您可以对以下内容进行投票表决:
trichoplax

5
最高周长的
多米诺

1
最高周长连接的多边形
trichoplax

N行正好是M个字符:如果发现某些输入方便,是否可以互换两个输入值?
级圣河

3
@steveverrill我已经编辑了“输出”部分。这符合您的要求吗?
trichoplax

Answers:


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.

结果有两种主要情况。如果大小中的至少一个是奇数,则该模式是普通的“ rake”。例如,用于输入7 6

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

如果两个大小均相等,则有一个额外的列,其中每隔一个正方形为“开”。例如,用于输入8 6

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

现在,为了显示这些模式达到问题描述中给出的理论上的最大周长,我们需要确认第一个模式具有周长(M + 1) * (N + 1),第二个模式具有相同的值减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)

对于第二种情况下都MN均匀,周边从加起来:

  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

我认为可以通过将锯齿状的部分放在左侧来节省几个字节。应该减少堆栈重排。但是该睡觉了……
Reto Koradi

5

Ruby,第1版,66

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

用于提高m0到1的幂来决定m #是打印1还是。

用于>测试最后一行而不是==

推杆后不能摆脱空间,也不能放任何括号!

Ruby,第0、69版

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

这是一个匿名lambda函数。像这样使用它:

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个#符号和一个#符号的交替行,并右对齐M个字符。

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

N个偶数的典型输出。5 6显然6 55 5由于底行的锯齿状,与增加垂直周长相比,其周长与相同,或者M + 1 = 6的增量。6 6具有相同6 5加在垂直周界的增量(M + 1)-1 = 6。因此它们符合公式。

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

Ruby rjust允许您指定用于空白单元格的填充非常方便。通常将padding设置为," "但是对于最后一行,我们切换到"# "(请注意,如果N为偶数,则仅在最后一行上需要填充。如果N为奇数,则最后一行将是完整的,并且没有理由,因此您看不到任何痕迹。)

在这里查看。


@ Vioz-感谢您的ideone!我将程序测试为低的N和M值,以查看是否存在边缘情况,但我没有费心检查它是否适用于那么高的值。显然nel骨和cr骨都是正确的,因此我将其保留。稍后再回来看看是否可以删除一些方括号和空格。
级圣河

链接没问题吗?我认为这对其他人很有用,因为我用它来测试:P关于拼写编辑,我将其更改为可以找到的第一个结果,因为我从未见过实际使用的单词。我不知道很多关于Ruby(没有,事实上),但你可以改变i%2==0,以i%2<1挽救一个字节(我做了这种变化的ideone链接)。
卡德2015年

您是否真的需要#最后一行填充?例如,在最后一个图中,没有#右下角的周长不是一样的吗?
Reto Koradi

@RetoKoradi确实是相同的周长-看起来代码包含了额外的内容,#只是因为它已经是每行结束的方式,所以它的字节数少于在其中放置空格的字节数。(虽然我不知道红宝石...)。
trichoplax

1
@trichoplax您的直觉是正确的。填充"# "不是" #"因为后者会给2个相邻#的奇数M,这绝对是不需要的。2个相邻#的连M都没有伤害,所以我同意了。我还没有尝试过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)

添加手指的过程从多边形中删除了一条边,并添加了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)

增加手的高度只会使位于第一个x索引和每个其他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都是最优的。

对于偶数M,我们用与奇数M相同的方式填充木板,但是我们在最后一个段中添加了锯齿状,例如:

4x3
# ##
# # 
####

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

现在我们证明该策略是最佳的。

偶数M的归纳法:
假设解对(N,M-1)是正确的,且奇数M-1(在最后一种情况中得到证明),其最佳周长为(N +1)M-( M(N + 1))mod 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
这是很多数学!您不能只计算所创建形状的周长,并显示出它与提供的最大值匹配吗?您知道您有多少“手指”,每个手指有多长,等等。因此,计算周长应该相当容易。
Reto Koradi

真正。在某些方面,我认为归纳路径更为直观,因为它是可加的,但是,确实会导致冗长的解释。
安德烈·哈德

您可能想知道周长等于其经过的整数点的数量。
jimmy23013 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.