Corte a corrente de ouro


32

Um viajante precisa ficar n dias em um hotel fora da cidade. Ele está sem dinheiro e seu cartão de crédito expirou. Mas ele tem uma corrente de ouro com n elos.

A regra neste hotel é que os residentes paguem o aluguel todas as manhãs. O viajante chega a um acordo com o gerente para pagar um elo da cadeia de ouro por dia. Mas o gerente também exige que o viajante cause o menor dano possível à cadeia e pague todos os dias. Em outras palavras, ele tem que encontrar uma solução para cortar o menor número possível de links.

Cortar um link cria três sub-cadeias: uma contendo apenas o link de corte e uma de cada lado. Por exemplo, cortar o terceiro elo de uma cadeia de comprimento 8 cria sub-cadeias de comprimento [2, 1, 5]. O gerente tem o prazer de fazer alterações, para que o viajante possa pagar o primeiro dia com a cadeia de comprimento 1, depois o segundo dia com a cadeia de comprimento 2, recuperando a primeira cadeia.

Seu código deve inserir o comprimento n e gerar uma lista de links para cortar no tamanho mínimo.

Regras :

  • n é um número inteiro> 0.
  • Você pode usar a indexação com base em 0 ou em 1 para os links.
  • Para alguns números, a solução não é única. Por exemplo, se n = 15ambos [3, 8]e[4, 8] são saídas válidas.
  • Você pode retornar a lista ou imprimi-la com qualquer separador razoável.
  • Isso é , então o código mais curto em bytes vence.

Casos de teste :

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

Exemplo detalhado

Para n = 15, cortar os links 3 e 8 resulta em sub-cadeias de comprimento [2, 1, 4, 1, 7]. Esta é uma solução válida porque:

 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ão existe solução com apenas um corte; portanto, essa é uma solução ideal.

Termo aditivo

Observe que esse problema está relacionado ao particionamento inteiro. Estamos à procura de uma partição P de n tal que todos os inteiros de 1 a n ter pelo menos um patition que é um subconjunto de P .

Aqui está um vídeo do YouTube sobre um possível algoritmo para esse problema.


Não entendo sua referência "fazendo alterações". No exemplo postado, no segundo dia você paga com a cadeia de 2 elos (e recupera a cadeia de 1 elo (que você pagou no dia anterior) como alteração, conforme sua explicação). Mas no terceiro dia, você paga com 1+2. De onde veio a segunda cadeia de 2 elos?
Flater 18/06

4
@Flater O gerente já o possui. Nós apenas pagamos o adicional. De fato, o RHS é o link que o gerente possui todos os dias
polfosol ఠ_ఠ 18/06

Respostas:


15

05AB1E , 23 11 8 bytes

ΔÍN-;иg=

Experimente online!

Usa indexação baseada em 0.

Explicação:

             # 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

иgparece um noop, mas na verdade faz duas coisas úteis: trunca para um número inteiro ( ;retorna um número flutuante) e trava o interpretador se x for negativo (esta é a única condição de saída).


A solução de 23 bytes usou uma abordagem muito diferente, então aqui está a posteridade: ÅœʒR2äθP}ʒæOê¥P}θ2äθη€O( TIO , explicação ).


2
Eu apaguei minha resposta. O meu ser 42 e o seu 11 é uma diferença muito grande para eu não me sentir envergonhada, haha. ;) Boa resposta, porém, e lol no Ø.Ø. Você tentou algumas coisas aleatórias para mostrar e mapear todos os negativos -1? Independentemente, resposta muito agradável e muito mais curta do que eu previa. Eu estava pensando em cerca de 20 bytes depois de postar meu 42-byter ruim.
Kevin Cruijssen 17/06

2
@KevinCruijssen Nnope, Ø.Øfoi realmente a minha primeira ideia. O seu comentário me inspirou a tentar coisas aleatórias: eu encontrei ®Ÿà, ï®Me, mais importante, иgque produz este simpático 8 Byter. Eu sempre achei irritante que osabie preferisse não fazer nada demais em muitos casos (div por 0, tipo errado, etc.), então essa falha será útil.
Grimmy 17/06

2
Hehe, 05AB1E nunca deve falhar, mas você está certo de que às vezes é um pouco chato que nunca. No legado, eu nem sabia como falhar, e no passado tínhamos uma divisão explícita de erro zero, poderíamos chamar manualmente. xD Na nova versão, ele ainda trava com um erro bastante frequente ao fornecer argumentos incorretos para certos componentes internos. E bom -3 novamente.
Kevin Cruijssen 18/06

2
"trava o intérprete se x for negativo (esta é a única condição de saída)." - Eu amo isso
John Dvorak

9

Python 2 , 75 bytes

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)]

Experimente online!


Explicação:

Cria uma sequência de blocos 'binários', com um número base correspondente ao número de cortes.

Por exemplo:

63 pode ser feito em 3 cortes, o que significa uma partição na base-4 (como temos 3 anéis únicos):

Cortes:, 5, 14, 31que fornece cadeias de 4 1 8 1 16 1 32(classificadas:) 1 1 1 4 8 16 32.

Todos os números podem ser feitos:

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

Outros exemplos:

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
Você não deve adicionar f=ao início? Como você usa uma chamada fna função lambda, e posso apenas assumir que você se refere à mesma lambda que está definindo.
randomdude999 17/06

@ randomdude999, Sim, eu esqueci ...
TFeld 17/06

@ randomdude999 essa regra se aplica a todos os idiomas ou apenas ao python? Porque eu vejo uma resposta javascript que é uma lambda pura neste desafio ...
Shadow

3
@ Shadow Aplica-se a todos os idiomas, mas apenas para lambdas recursivas.
TFeld 18/06

1
@Shadow A more generic rule is that you can't reference something that is neither defined in your code nor globally defined in your language, unless it is explicitly allowed by the challenge. The most common case is a recursive function, but this applies to other situations. This answer is another example: f is not recursive, but it's however referenced in the code and therefore has to be named.
Arnauld

8

R, 77 69 bytes

-8 bytes thanks to Aaron Hayman

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

Try it online!

Let k be the number of cuts needed; k is the smallest integer such that (k+1)2kn. Indeed, a possible solution is then to have subchains of lengths 1,1,,1 (k times) and (k+1),2(k+1),4(k+1),8(k+1),,(k+1)2k1. It is easy to check that this is sufficient and optimal.

(The last subchain might need to be made shorter if we exceed the total length of the chain.)

Ungolfed (based on previous, similar version):

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

If successful, remember 1 more than the input on the previous line, increment N on the current line, and update the input to the new value.

1+,
1

Remove N and increment the last value so that it's also 1-indexed.

1A`

Remove the incremented original input.

1+
$.&

Convert the results to decimal.


1

Ruby, 62 bytes

->n{(1...c=(0..n).find{|r|n<r<<r}).map{|b|[n,b-c+(c<<b)].min}}

Try it online!

Mostly stolen from TFeld's Python answer.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.