您应该如何安排椅子?


20

您会教一类学生,他们对椅子的布置有有趣的偏好。他们对椅子的布置有3个非常具体的要求:

  1. 它们大多数都以矩形排列,即使这意味着有些椅子是空的。

  2. 空椅子必须尽可能少。

  3. 它们必须尽可能“方形”。矩形度取决于矩形的宽度和高度之间的距离,越低越好。例如,矩形4x7的正方形度为3。

更具体地说,布置的“分数”是宽度和高度之间的距离加上将要空的椅子数量。

让我们举个例子。假设您有13个学生。您可以通过以下任何一种方式安排椅子:

1x13
2x7
3x5
4x4

1x13不是很方形。实际上,1和13相距12点,因此我们给此排列12点。它还有0个空椅子,所以我们加0分,使这种安排的得分为12。

2x7当然更好。2和7仅相距5,所以我们给这种安排5分。但是,如果实际上安排了两排,每排有七把椅子,那将占用14把椅子,这意味着一把椅子将是空的。因此,我们加1分,使该安排获得6分。

我们也可以做3x5。3和5相距2,所以+2分。它需要15张椅子,这意味着我们要再增加两把椅子,所以再加上2分,得到4分。

最后一个选择4x4。4和4相距0,因此我们给了+0分。4x4需要16张椅子,所以3张椅子空了,总分3。这是最佳的解决方案。

如果是平手,最佳的解决方案是空椅子少的人。

挑战

您必须编写一个程序或函数,该程序或函数需要一个整数,并为该数量的学生输出最佳的椅子布置。IO可以采用任何合理的格式。这是从1到100的任意数量学生的示例输出:

1:  (1, 1)
2:  (1, 2)
3:  (2, 2)
4:  (2, 2)
5:  (2, 3)
6:  (2, 3)
7:  (3, 3)
8:  (3, 3)
9:  (3, 3)
10: (2, 5)
11: (3, 4)
12: (3, 4)
13: (4, 4)
14: (4, 4)
15: (4, 4)
16: (4, 4)
17: (3, 6)
18: (3, 6)
19: (4, 5)
20: (4, 5)
21: (3, 7)
22: (5, 5)
23: (5, 5)
24: (5, 5)
25: (5, 5)
26: (4, 7)
27: (4, 7)
28: (4, 7)
29: (5, 6)
30: (5, 6)
31: (4, 8)
32: (4, 8)
33: (6, 6)
34: (6, 6)
35: (6, 6)
36: (6, 6)
37: (5, 8)
38: (5, 8)
39: (5, 8)
40: (5, 8)
41: (6, 7)
42: (6, 7)
43: (5, 9)
44: (5, 9)
45: (5, 9)
46: (7, 7)
47: (7, 7)
48: (7, 7)
49: (7, 7)
50: (5, 10)
51: (6, 9)
52: (6, 9)
53: (6, 9)
54: (6, 9)
55: (7, 8)
56: (7, 8)
57: (6, 10)
58: (6, 10)
59: (6, 10)
60: (6, 10)
61: (8, 8)
62: (8, 8)
63: (8, 8)
64: (8, 8)
65: (6, 11)
66: (6, 11)
67: (7, 10)
68: (7, 10)
69: (7, 10)
70: (7, 10)
71: (8, 9)
72: (8, 9)
73: (7, 11)
74: (7, 11)
75: (7, 11)
76: (7, 11)
77: (7, 11)
78: (9, 9)
79: (9, 9)
80: (9, 9)
81: (9, 9)
82: (7, 12)
83: (7, 12)
84: (7, 12)
85: (8, 11)
86: (8, 11)
87: (8, 11)
88: (8, 11)
89: (9, 10)
90: (9, 10)
91: (7, 13)
92: (8, 12)
93: (8, 12)
94: (8, 12)
95: (8, 12)
96: (8, 12)
97: (10, 10)
98: (10, 10)
99: (10, 10)
100: (10, 10)

像往常一样,这是代码高尔夫球,因此存在标准漏洞,而获胜者是最短的答案(以字节为单位)。


Answers:


8

果冻16 15 14 字节

÷RĊ,Rµạ/+PỤḢịZ

在线尝试!验证所有测试用例

怎么运行的

÷RĊ,Rµạ/+PỤḢịZ  Main link. Argument: n

 R              Range; yield [1, ..., n].
÷               Divide n by each k in [1, ..., n].
  Ċ             Ceil; round the quotients up to the nearest integer.
    R           Range; yield [1, ..., n].
   ,            Pair; yield A := [[ ⌈n ÷ 1⌉, ..., ⌈n ÷ n⌉ ], [ 1, ..., n ]].
     µ          Begin a new, monadic chain. Argument: A
      ạ/        Reduce A by absolute difference.
                This yields [ |⌈n ÷ 1⌉ - 1|, ..., |⌈n ÷ n⌉ - n| ].
         P      Product; reduce A by multiplication.
                This yields [ ⌈n ÷ 1⌉ × 1, ..., ⌈n ÷ n⌉ × n].
       +        Add the results to left and right, element by element. This yields
                [ |⌈n ÷ 1⌉ - 1| + ⌈n ÷ 1⌉ × 1, ..., |⌈n ÷ n⌉ - n| + ⌈n ÷ n⌉ × n ].
          Ụ     Grade up; sort the indices of the list of sums by their values.
           Ḣ    Head; extract the first value, which corresponds to the smallest
                sum. Grading up is stable, so this selects the first index of all
                with the smallest sum in case of a tie. In this event, the first
                index will have the highest absolute difference of all indices
                with the smallest sum, meaning that it has the lowest product and,
                therefore, the lowest number of empty chairs.
             Z  Zip; transpose A's rows and columns.
                This yields [[ ⌈n ÷ 1⌉, 1 ], ..., [ ⌈n ÷ n⌉, n ]].
            ị   Retrieve the pair at that index.

4

Python 2,68个字节

lambda n:min((abs(~i-n/~i)+n/~i*~i,i+1,0-n/~i)for i in range(n))[1:]

相当于更“明显”的:

lambda n:min([(i+1,0-n/~i)for i in range(n)],key=lambda(p,q):abs(p-q)+p*q)

range(-n,0)就像我在回答中所做的那样,可以通过迭代来节省三个字节。测试套件。
丹尼斯

3

Haskell,65个字节

f x=snd$minimum[((a*b+a-b,a*b),(b,a))|a<-[1..x],b<-[1..a],a*b>=x]

用法示例:map f [1..5]-> [(1,1),(1,2),(2,2),(2,2),(2,3)]

经历一个a1到的外部循环x(x->学生人数)和一个b1到的内部循环a。保留所有(b,a)位置a*b>=x并建立对,((arrangement points,seats left), (b,a))这些对遵循我们所需的最小字典顺序。注意:a总是大于b,因此我们不需要abs矩形。无需x从“左座”分数中减去,因为只有相对顺序很重要。最后,我们用删除得分对snd


为什么不只是(a b + ab,(b,a))?如果您将分数最小化,那么肯定可以将 b 最小化,还是我错过了什么?
justinpc '16

@jpcooper :(a*b主要席位相等)(免费席位)是决胜局。例如n=43:a)a=7, b=7,得分:(49,49)b)a=9, b=5,得分:(49,45)。主要比分相等,决胜局决定,b)获胜。
nimi

你是对的。我应该更好地阅读说明。
justinpc '16

@jpcooper:等一下...如果我拆下领带开关a*b(b,a)无论如何我必须携带的数字本身将作为领带开关并至少给出相同的结果n=1..300。如果其中一个因素较小,则产品b较小。但是,只要我没有正式的证据,我就不想使用这个事实。让我们看看是否找到一个。
nimi

好点子。看来是正确的,应该不难提出一个证明。我开始怀疑,对于这个问题是否可能存在线性解决方案。
justinpc '16

2

Ruby,64个字节

->n{(1..n).map{|w|h=(n+w-1)/w;[(h-w).abs+h*w,w*h,w,h]}.min[2,3]}

以人数作为参数并返回具有最佳解决方案宽度和高度的数组的lambada。


为什么需要w*h作为数组中的第二个元素?我认为通话时它不会改变任何东西,min因为您可以将得分(即第一个元素)最小化。
价值墨水

@ KevinLau-notKenny来自问题:In case of a tie, the optimal solution is the one with less empty chairs
MegaTom,

2

MATL,18字节

:Gy/Xkvtd|yp+&X<Z)

在线尝试!

说明

:      % Implicit input number N. Range [1 2 ... N]
G      % Push N again
y      % Duplicate second-from-top: push [1 2 ... N] again
/Xk    % Divide and round up
v      % Vertically concatenate. Gives 2×N array of rectangle sizes
td|    % Duplicate. Absolute difference of each column
y      % Duplicate second-from-top: push 2×N array again
p      % Product of each column
+      % Sum absolute differences and products
&X<    % Arg min
Z)     % Use as column index into the 2×N array. Implicitly display

2

Javascript,98个字节

我的第一个代码高尔夫,所以我还是要发布!

f=n=>{for(o=1/0,i=1;i<=n;i++)for(j=n;i*j>=n;j--)t=i*j-n+Math.abs(i-j),o>t&&(o=t,a=[i,j]);return a}

最初,我o是一个空对象,我检查是否o.a为空,因此在第一轮中是一种特殊情况。但是我在edc65的答案中发现了1/0技巧,将变量初始化为Infinity。


我将尝试使用一个对象来存储临时结果的技巧
edc65 '16

1

Pyth,24 22 21字节

编辑:在排序键中,我意识到无需查找空椅子的数量。它等于对椅子总数进行评分。这为我节省了2个字节。

h.m_+B*FbaFbm,d.EcQdS

在线尝试!


1

Matlab的(174)(146)121

  function g(n),f=@(n,i)ceil(n/i);x=[];for i=1:n,x=[sortrows(x); f(n,i)*i-1/(f(n,i)*i)+abs(f(n,i)-i) i f(n,i)];end,x(1,2:3)
  • 技巧1:我将金额添加1-1/length*width为平局得分

  • 技巧2:我计算出number_students/length天花板的矩形宽度,上限是正方形,但天花板也是如此

  • 我相信它可以打得更远...

试试吧


编辑:引用@StewieGriffin的言论。

编辑2:

  • 1并且n是常量,无需将'em添加到总分中。
  • 一个函数比标准输入独立程序少几个字节。
  • 我使用了提升排序技术,但它节省了太多字节。

编辑3:性能测试。


@StewieGriffin没什么大问题,可以使用unique
Abr001am

1
我认为此问题尚需进行一些不错的数学翻译,但仍处于推测阶段
Abr001am

也想到了这一点。请参阅julia示例。
mschauer '16


1

朱莉娅61 59 55 53 52字节

/ =cld
n->[m=indmax([~i*~-max(i,n/i)for i=1:n]),n/m]

在线尝试!

怎么运行的

该代码等效于以下非高尔夫版本,其中cld是天花板划分。

function chairs(n)
    m = indmin([(i + 1) * (max(i, cld(n, i)) - 1) for i in 1:n])
    return [m, cld(n, m)]
end

找到最优配置,这显然是足够的检查对[I,J] ,其中1≤I≤ÑJ =⌈n/i⌉

这样的安排的分数是| j-i | +(ij-n),其中第二个被乘数是空椅子的数量。代替实际分数,我们可以比较以常数增加的分数,例如ij + | j-i | +1

这足以考虑对[I,J]这里我≤Ĵ因为安排[I,J][J,I]同样有效。我们通过设置处理严格下降对J = MAX(⌈n/i⌉,I)代替,这可以确保Ĵ≥我和将产生不理想的分数,如果⌈n/i⌉<我

由于j-i≥0,我们有ij + | j-i | + 1 = ij + j-i + 1 =(i + 1)×(j-1),可以用更少的代码字节来计算。

最后,indmin/ indmax给出最优排列的索引m(以及i的值),即m⌈n/m⌉。领带由于第一次出现而破裂,这对应于i的最小值,因此对应于j-i的最大值,因此也等于ij-n(空椅子)的最小值。


1

JavaScript(ES6)74 78

编辑将临时结果保留为数组,而不是从Thiht的答案借来的2个变量

n=>(z=>{for(x=0;y=-~(~-n/++x),x<=y;)(s=y-x+x*y-n)>=z||(z=s,r=[x,y])})()||r

少打高尔夫球

n=>{
  z = 1/0
  for (x=0; y=(n-1)/++x+1|0, x <= y; )
  {
    s = y-x+x*y-n;
    if (s<z)
      z=s, r=[x,y]
  }
  return r
}

测试

f=n=>(z=>{for(x=0;y=-~(~-n/++x),x<=y;)(s=y-x+x*y-n)>=z||(z=s,r=[x,y])})()||r

out=x=>O.textContent+=x+'\n'

for(i=1;i<=100;i++)out(i+' :( '+f(i)+' )')
<pre id=O></pre>


1

PHP,129字节

function f($i){$s=INF;for($x=1;$x<$i;$x++){if($s>$t=(abs($x-$e=ceil($i/$x))-$i+($e*$x))){$s=$t;$d[0]=$x;$d[1]=$e;}}var_dump($d);}

取消高尔夫:

function f ($i){
    $s=INF;
    for($x=1; $x<$i; $x++){ // for every number less than the input
        if( $s > $t=( abs($x-$e=ceil($i/$x))-$i+($e*$x) ) ){ 
            // determine the other dimension, the score, and compare to the minimum score
            $s=$t;
            $d[0]=$x;
            $d[1]=$e;
        }
    }
    var_dump($d);
}

1

PHP,104字节

解决此问题的算法很简单,其他答案可能会以类似于PHP(JavaScript,fe)的语言来使用:

  • 从较大的初始分数开始;n足够大(n输入值在哪里);在第一次迭代中计算出的排列分数(1, n)是(n-1)+0;
  • 迭代1和之间的所有width值n。计算最小高度为ceil(n/width),使用问题中提供的公式计算布置分数(即abs(width - height) + (width * height - n));如果该分数比先前的最佳分数更好,则记住宽度,高度和新的最佳分数;在关系上,使用width * height - n当前安排和先前最佳安排的值来检测新的最佳安排;
  • 就这样。

打完高尔夫球之后,此算法将产生如下内容(为便于阅读,在此处进行了包装):

for($s=$h=$j=$n=$argv[$w=$i=1];$i<=$j;$j=ceil($n/++$i)
{$c=$j-$i+$i*$j-$n;if($c<$s||$c==$s&&$i*$j<$w*$h){$w=$i;$h=$j;$s=$c;}}
echo"$w,$h";

它使用137个字节(放在一行中),并且与标题中广告的104个字节相距甚远。可以将代码再缩短2-3个字节,但是改进的最大源头是其他地方:算法的细节。

修改后的算法:

在一些地方,可以通过删除无用的代码来改进算法。

  • 没有必要从迭代的宽度1$n; 为了提高速度,宽度($i)必须在1和之间进行迭代,floor(sqrt($n))但这会使代码更长而不是缩短。但是如果宽度不超过sqrt($n),则最小高度($j)将始终大于sqrt($n)(其乘积必须至少$n);
  • 前面的语句允许使用$i <= $j(width <= height)作为循环的终止条件;这样,宽度将从迭代1floor(sqrt($n)),高度将得到以开头$n和向下的值ceil(sqrt($n))(不一定是所有值);
  • 知道宽度总是小于或等于高度时,我们就知道abs(width - height)总是height - width$j-$i);这样保存了5个字节;
  • 输入值$n用于计分(空座数为width * height - n),但不需要;分数不需要显示,仅用于比较安排而计算;通过- n从得分公式中删除,我们又节省了3个字节(PHP代码为-$n),而不会丢失任何内容;
  • 给定最后两个语句,得分公式变为height - width + width * height$j-$i+$i*$j);
  • 在平局上(当前安排的分数与之前的最佳分数相同),规则说使用自由座位较少的安排;因为宽度总是增加而高度总是减少,所以height - width所以分数的每一步都会减少。
  • 如果当前分数等于先前的最佳分数,则先前的陈述告诉我们,当前安排的免费座位数大于先前的最佳安排之一;这意味着以前的最佳安排赢得了平局;
  • 因为总是由先前的最佳安排赢得平局,所以只有当其得分小于先前的最佳成绩时,新的安排才成为新的最佳安排。检查联系的代码是无用的,可以删除(||$c==$s&&$i*$j<$w*$h-很多字节);
  • 由于-$n从得分的公式中删除了,第一个排列(1x$n)的得分为$n-1+1*$n(即2*$n-1);最佳分数($s)的初始值可以是大于或等于任何值2*$n;第一次迭代的得分更高,它成为最佳的安排,使算法可以运行而不会出现初始化问题。

在应用上述改进之后,新代码(104字节)为:

for($s=2*$j=$n=$argv[$i=1];$i<=$j;$j=ceil($n/++$i))
if($s>$c=$j-$i+$i*$j){$w=$i;$h=$j;$s=$c;}echo"$w,$h";

为了便于阅读,将其包装在此处。在上面的代码之前加上PHP标记<?php(从技术上讲,它不是代码的一部分),将其放入文件中(假设arrange-your-chairs.php),并使用大于零的整数作为参数运行它。它将显示计算的排列的宽度和高度,以逗号分隔:

$ php arrange-your-chairs.php 1001
28,36

另一种解决方案(116字节)

使用不同算法的另一个解决方案:

for($n=$argv[1];++$j<=$n;)for($i=0;++$i<=$j;)
if($n<=$k=$i*$j)$a["$i,$j"]=($j-$i+$k-$n)*$n+$k;asort($a);echo key($a);

它将至少$n席位的所有组合放入关联列表;关键是布置的文本表示形式,值是布置的分数。然后,它对列表进行排序(按值升序)并获取第一个条目的键。

再多一个(115字节)

foreach(range(1,$m=$n=$argv[1])as$i)
if(($d=ceil($n/$i))<=$i&&$m>=$s=$i*$d-$n+$i-$d){$m=$s;$w=$d;$h=$i;}echo"$w,$h";

这是@Neil 答案的PHP版本(JavaScript / ES6,85字节)。

由于每种语言的功能,存在一些明显的差异:

  • JS答案生成一个n(未定义)值的数组,然后使用其键从迭代0n-1;它增加id=(n+i++)/i|0),以从使其迭代1n; PHP解决方案不需要增加;它使用range()以产生阵列然后使用所产生的值(1n)进行迭代;
  • JS答案使用,(n+i)/i然后将值转换为,使用|0以获得大于的最小整数n/i;PHP答案通过PHP函数轻松解决了这个问题ceil(); JavaScript还提供了Math.ceil()但比Neil找到的解决方案多使用了5个字节;
  • PHP提供的功能array_map()在某种程度上与JS类​​似,Array.map()但在此无济于事。它的语法很冗长foreach产生较短的代码;但是,它比JS代码大;
  • 使用以下命令将作业合并到条件中 ||在PHP中不可能,因为它缺少逗号运算符;我将其翻译a||b||cif(!a&&!b)c,因为ab是比较,所以否定了它们的运算符(用代替<>=);这也会产生比JS版本更大的代码;
  • 仅由于PHP中的变量名必须以前缀为前缀,就必须再添加23个字节$

可以找到所有解决方案和测试套件的原始版本 在Github


1
这是我见过的最彻底的代码高尔夫球答案。
DJMcMayhem

0

JavaSCript(ES6),83个字节

n=>[...Array(m=n)].map((_,i)=>(d=(n+i++)/i|0)>i||(s=i*d-n+i-d)>m||(m=s,r=[d,i]))&&r

也许您可以套用我的把戏(节省2个字节)
Leaky Nun

@KennyLau我认为这没有帮助;我必须增加m补偿。
尼尔

0

朱莉娅87岁

我认为这是朝着找到该问题的魔术函数迈出的一步:

f(i)=(i+n)÷(i+1)|>j->(j*i<n)+j
_=indmin([sqrt(n)<=i?i-f(i)*(1-i):2n for i=1:n])
_,f(_)

它只看成对(i, j=(i+n)/(i+1))(i, j+1)


请进一步说明该工作原理,您让我好奇您的功能
Abr001am,2013年

2
我不确定这应该如何工作。您没有n在任何地方进行定义,而且似乎也没有接受输入。
丹尼斯

啊,对不起,我只是n作为输入。需要将其包装到中n->...。很高兴您可以使它正常工作。
mschauer

0

Oracle SQL 11.2,173个字节

SELECT MIN(x||','||y)KEEP(DENSE_RANK FIRST ORDER BY y-x+(y*x-:1))FROM(SELECT CEIL(LEVEL/:1)x,CEIL(MOD(LEVEL+.1,:1))y FROM DUAL CONNECT BY LEVEL<=:1*:1)WHERE x<=y AND:1<=x*y;

未打高尔夫球

SELECT MIN(x||','||y)KEEP(DENSE_RANK FIRST ORDER BY y-x+(y*x-:1))  -- Keeps the minimal score
FROM   (SELECT CEIL(LEVEL/:1)x,CEIL(MOD(LEVEL+.1,:1))y FROM DUAL CONNECT BY LEVEL<=:1*:1) -- Generate x,y combinations 
WHERE  x<=y AND :1<=x*y  -- Filters out wrong combinations

0

Q 58字节

{c@d?&/d:+/(-/;*/)@\:+c:{((b<a)?1b)#+(b:-_-x%a;a:1+!x)}x}

Lamba计算给定值(x)的最小成本并返回两个值(宽度,高度)的序列

在该lambda上添加名称需要其他两个字符(例如f:{..},而不是{..})

测试

{..}'1+!100

其中{..}是lambda。读为“将lambda应用于1+前100个整数的每个值”(换句话说,将lambda应用于每个值1..100)

产生

1 1
2 1
2 2
2 2
3 2
3 2
3 3
3 3
3 3
5 2
4 3
4 3
4 4
4 4
4 4
4 4
6 3
6 3
5 4
5 4
7 3
5 5
..

说明

嵌套的lamdba {((b<a)?1b)#+(b:-_-x%a;a:1+!x)}生成x椅子的所有候选(宽度,高度)对,这两个对是两个序列(宽度和高度)(w1 w2 w3 ..; h1 h2 h3 ..)。从左到右读取,但从右到左求值

a:1+!x 生成值1..x并将该序列分配给

-_- 是negate floor negate,并实现ceil(ceil不是该语言的原始语言)

b:-_-x%a将ceil应用于x的每个值除以im a的任意项,并将结果序列分配给b。换句话说,b是每个x除以1..x

+(b;a) 返回由seq a和seq b组成的安全性,然后翻转它(结果是一对序列,其中i-pair包含a的元素i和b的元素i)

b<a 比较b和a的各项,并生成逻辑值的安全性(对于每个索引,true = 1b,其中b [i]

s?x返回序列x中项x的第一个位置。随着(b<a)?1b我们期待在序列1B(真值)比较,B和A的产生,并获得第一位置,其中b

n#s从seq中获取n个前n个项。我们要舍弃重复的对,因此当对中的第一项<第二项时(例如,考虑13,1而不是1,13),我们将停止。

副作用是,每对结果序列在a和b之间的距离减小(例如(13 1; 7 2; 5 3; 4 4)

嵌套lambda生成的候选对分配给c。然后,我们翻转c(再次获得b,a)并将两个函数应用于该参数:*/相乘和-/相减。结果(-/;*/)@\:+c是每对的差和乘积。+/是总和,而calculares的最终成本。每个巡逻者的费用分配给d

&/是最小值,因此&/d是最小成本。随着d?&/d我们发现最小的成本在d,与C的第一次出现@ ..我们获取对在该位置。由于每个对在a和n之间的距离都在减小,因此找到的第一个最小值在其他最小值对之间具有最大距离,因此我们可以正确应用平局规则

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.