切割金链


32

旅行者需要在城外的一家酒店住宿n天。他没有现金了,他的信用卡已过期。但是他有一个带有n个链接的金链。

这家酒店的规定是居民应每天早晨支付租金。旅行者与经理达成协议,每天支付金链的一个链接。但是经理还要求旅行者在每天付款时,对链条的损坏应尽可能少。换句话说,他必须提出一种解决方案,以减少尽可能少的链接。

剪切链接将创建三个子链:一个仅包含剪切链接的子链,而在每一侧各包含一个。例如,剪切长度为8的链的第三个链接将创建长度为[2,1,5]的子链。经理很高兴进行更改,因此旅行者可以在第一天使用长度为1的链条进行支付,然后在第二天使用长度为2的链条进行支付,从而收回第一条链条。

您的代码应输入长度n,并输出链接列表以切出最小长度。

规则

  • n是大于0的整数。
  • 您可以对链接使用基于0或基于1的索引。
  • 对于某些数字,解决方案不是唯一的。例如,如果n = 15两个[3, 8][4, 8]是有效的输出。
  • 您可以返回列表,也可以使用任何合理的分隔符进行打印。
  • 这是,因此以字节为单位的最短代码获胜。

测试用例

Input          Output (1-indexed)
1              []
3              [1]
7              [3]
15             [3, 8]
149            [6, 17, 38, 79]

详细的例子

对于n = 15,剪切链接3和8会得到length的子链[2, 1, 4, 1, 7]。这是一个有效的解决方案,因为:

 1 = 1
 2 = 2
 3 = 1+2
 4 = 4
 5 = 1+4
 6 = 2+4
 7 = 7
 8 = 1+7
 9 = 2+7
10 = 1+2+7
11 = 4+7
12 = 1+4+7
13 = 2+4+7
14 = 1+2+4+7
15 = 1+1+2+4+7

没有一个解决方案,只有一个切口,因此这是一个最佳解决方案。

附录

请注意,此问题与整数分区有关。我们正在寻找n的分区P,以使从1到n的所有整数都至少具有一个子集,该子集是 P

这是一个有关该问题的可能算法的YouTube视频


我不理解您的“做出改变”参考。在您发布的示例中,第二天您使用2链接链付款(并根据说明将1链接链(您在前一天付款的)取回作为零钱)。但是在第三天,您用支付1+2。第二条2链从何而来?
6

4
@Flater经理已经有了它。我们只需支付额外的费用。实际上,RHS是经理每天拥有的链接
polfosolఠ_ఠ

Answers:


15

05AB1E23 11 8字节

ΔÍN-;иg=

在线尝试!

使用基于0的索引。

说明:

             # start from the implicit input
Δ            # loop forever
 Í           # subtract 2
  N-         # subtract the current iteration number
    ;        # divide by 2
     и       # create a list of length x
      g      # get the length of the list
       =     # print

иg看起来像一个noop,但实际上它做两件事:将其截断为整数(;返回浮点数),如果x为负(这是唯一的退出条件),它将使解释器崩溃。


23字节的解决方案使用了一种非常不同的方法,因此这里是为后代使用的ÅœʒR2äθP}ʒæOê¥P}θ2äθη€OTIO说明)。


2
我已删除答案。我的年纪是42岁,而你的年纪是11岁,对我来说太大了,以至于不感到尴尬,哈哈。;)不错的答案,然后笑了Ø.Ø。您是否只是尝试了一些随机的操作以将所有底片都覆盖并映射到-1?无论如何,答案非常好,比我预期的要短得多。发布差劲的42-byter之后,我在思考20个字节。
凯文·克鲁伊森

2
@KevinCruijssen Nnope,Ø.Ø实际上是我的第一个想法。您的评论促使我尝试随机的东西:我发现®Ÿàï®M以及更重要的是иg,这将产生这个漂亮的8 byter。我总是感到很讨厌,在很多情况下(比分是0,错误的类型等),osabie宁愿不执行任何操作而不是崩溃,因此此崩溃将派上用场。
Grimmy

2
呵呵,05AB1E应该永远不会崩溃,但是您说对了,有时候它有时会很烦人。零错误,我们可以手动调用。xD在新版本中,当给某些内置函数不正确的参数时,它仍然经常会因错误而崩溃。再好-3。
凯文·克鲁伊森

2
“如果x为负(这是唯一的退出条件),则破坏解释器。” -我喜欢
John Dvorak

9

Python 2,75个字节

f=lambda n,i=0:n>=i<<i and f(n,i+1)or[min(n,2**j*i-i+j)for j in range(1,i)]

在线尝试!


说明:

构建一个“二进制”块序列,其基数与剪切数匹配。

例如:

63 可以进行3次切割,这意味着在base-4中进行分区(因为我们有3个单环):

削减:5, 14, 31,可得到的链4 1 8 1 16 1 32(已排序:)1 1 1 4 8 16 32

所有数字均可:

1       1
2       1 1
3       1 1 1
4       4
...
42      1 1 8 32
...
62      1 1 4 8 16 32
63      1 1 1 4 8 16 32

其他例子:

18: 4,11        ->  3 1 6 1 7
27: 5,14,27     ->  4 1 8 1 13 1
36: 5,14,31     ->  4 1 8 1 16 1 5
86: 6,17,38,79  ->  5 1 10 1 20 1 40 1 7

1
您不应该添加f=开始吗?由于您f在lambda函数中使用调用,因此我只能假定您引用的是您定义的同一lambda。
randomdude999

@ randomdude999,是的,我忘了...
TF场

@ randomdude999该规则适用于所有语言还是仅适用于python?因为我在这个挑战中看到一个纯正的lambda的javascript答案
Shadow

3
@Shadow它适用于所有语言,但仅适用于递归lambda。
TF场

1
@Shadow一个更通用的规则是,除非挑战明确允许,否则您不能引用代码中未定义或语言中未全局定义的内容。最常见的情况是递归函数,但这适用于其他情况。这个答案是另一个例子:f不是递归的,但是在代码中引用了它,因此必须命名。
Arnauld

8

R77 69字节

-8字节归功于Aaron Hayman

pmin(n<-scan(),0:(k=sum((a=2:n)*2^a<=n))+cumsum((k+2)*2^(0:k))+1)[-n]

在线尝试!

ķķ 是最小整数,使得 ķ+1个2ķñ。确实,然后可能的解决方案是拥有一定长度的子链1个1个1个ķ 次)和 ķ+1个2ķ+1个4ķ+1个8ķ+1个ķ+1个2ķ-1个。可以很容易地检查出它是否足够且最佳。

(如果我们超过了链的总长度,则可能需要缩短最后一个子链。)

Ungolfed(基于以前的类似版本):

n = scan()                            # read input
if(n - 1){                            # If n==1, return NULL
  k = match(F, (a = 2:n) * 2 ^ a > n) # compute k
  b = (k + 1) * 2 ^ (1:k - 1)         # lengths of subchains
  c = 1:k + cumsum(b)                 # positions of cuts
  pmin(c, n )                         # if last value is >n, coerce it to n
}

(Proof that the value of k is as I state: suppose we have k cuts. We then have k unit subchains, so we need the first subchain to be of length k+1. We can now handle all lengths up to 2k+1, so we need the next one to be of length 2k+2, then 4k+4... Thus the maximum we can get out of k cuts is obtained by summing all those lengths, which gives (k+1)2k1.)

If a(k) is the smallest integer n requiring k cuts, then a(k) is OEIS A134401.


I doubt it would help with the special case for n=1, but an alternative way to generate the cutoffs is the recurrence 1, 4, 4a(n-1)-4a(n-2).
Peter Taylor

@PeterTaylor I had a similar recurrence for computing k; this corresponds to OEIS A134401: oeis.org/A134401 But my implementation of the recurrence relation takes up more bytes than the current code.
Robin Ryder

A bit of rearrangement I got it down to 73. Try it online!
Aaron Hayman

@AaronHayman Thanks! Smart move using sum instead of match.
Robin Ryder

69 bytes and got rid of that if statement that was upsetting you: Try it online!
Aaron Hayman



2

C++, 109,107 bytes

-2 bytes thanks to Kevin

#include<iostream>
main(){int n,k=0;for(std::cin>>n;++k<<k<n;);for(n-=k;n>0;k*=2,n-=k+1)std::cout<<n<<',';}

The algorithm is similar to the Robin Ryder's answer. The code is written in a compilable, whole form. Try it!

Details:

std::cin>>n;               // get the value of n as input
while(++k<<k<n);           // determine k
for(n-=k;n>0;k*=2,n-=k+1)  // we don't need n, so the lengths...
    std::cout<<n<<' ';     // of links are subtracted repeatedly

This has a C variation with the same byte length (doesn't seem to need a separate answer):

#include<stdio.h>
main(){int n,k=0;for(scanf("%d",&n);++k<<k<n;);for(n-=k;n>0;k*=2,n-=k+1)printf("%d,",n);}

Two minor things to golf: =0 after k can be removed, since its 0 by default. std::cin>>n;while(++k<<k<n); can be for(std::cin>>n;++k<<k<n;);. I also have the feeling for(n-=k;n>0;k*=2,n-=k+1) can be simplified somehow by combining stuff, but not sure how. PS: Changing the comma-delimiter to a space looks slightly better since you don't see the trailing one imo, but this is purely cosmetic :)
Kevin Cruijssen

1
@KevinCruijssen Thanks, but some compilers don't assign a default value to non-static variables. So I thought =0 was necessary for portability ;) I also realized that the space after #include is not necessary.
polfosol ఠ_ఠ

Ah ok. I don't know C++ too well, so I've used that online compiler you linked in your answer to test some things. :) You forgot the second change I proposed in my comment: the while-loop to a for-loop and putting the std::cin>>n inside it.
Kevin Cruijssen


1

Retina 0.8.2, 61 bytes

.+
11,$&$*
+`\b(1+),(\1(1*)1?\3)$
1$2¶1$1,$3
1+,
1
1A`
1+
$.&

Try it online! 1-indexed port of @Grimy's answer. Explanation:

.+
11,$&$*

Start with N=2 and the input converted to unary.

+`\b(1+),(\1(1*)1?\3)$

Repeatedly try to subtract N from the input and then divide by 2.

1$2¶1$1,$3

如果成功,请记住比上一行输入多1,N在当前行上递增,然后将输入更新为新值。

1+,
1

删除N并增加最后一个值,使其也为1索引。

1A`

删除增加的原始输入。

1+
$.&

将结果转换为十进制。


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.