Problema de divisão de colar

19

fundo

Fui inspirado pelo vídeo recente do 3Blue1Brown sobre o problema de divisão de colar (ou como ele chama, o problema de colar roubado) e sua relação com o teorema de Borsuk-Ulam .

Neste problema, dois ladrões roubaram um colar valioso que consiste em vários tipos diferentes de jóias. Há um número par de cada tipo de joia e os ladrões desejam dividir cada tipo de joia igualmente entre os dois. O problema é que eles devem fazê-lo dividindo o colar em um número de segmentos contíguos e distribuir os segmentos entre os dois.

Aqui é um exemplo, com quatro tipos de jóias denotados S, E, D, e R(por safira, esmeralda, diamante, e rubi, respectivamente). Digamos que o colar é o seguinte:

[S,S,S,E,S,D,E,R,S,R,E,S,S,S,D,R,E,E,R,E,D,E,R,R,D,E,E,E]

Existem 8safiras, 10esmeraldas, 4diamantes e 6rubis. Podemos dividir o colar da seguinte maneira:

[[S],[S],[S,E,S,D,E,R,S],[R,E,S,S,S,D,R,E,E,R,E,D,E],[R,R,D,E,E,E]]

Então, se dermos o primeiro, o terceiro e o quinto segmentos para um ladrão e o segundo e o quarto segmentos para o outro ladrão, cada um terminará com 4safiras, 5esmeraldas, 2diamantes e 3rubis:

[S],    [S,E,S,D,E,R,S],                            [R,R,D,E,E,E]
    [S],                [R,E,S,S,S,D,R,E,E,R,E,D,E],

Usando 0-indexing, esses cortes ocorrem nos índices [1,2,9,22].

Objetivo

Acontece que essa divisão justa sempre pode ser feita usando, no máximo n, cortes, onde nestá o número de tipos de jóias. Sua tarefa é escrever um programa ou função completa que use um colar como entrada e produza uma divisão mínima (menor número de cortes).

Entrada

A entrada pode estar em qualquer formato conveniente. O colar deve ser uma sequência de jóias e nada mais; por exemplo, uma lista de números inteiros, dicionário com chaves que representam os tipos e valores de joias, sendo listas de índices. Como opção, você pode incluir o comprimento do colar ou o número de tipos distintos de joias, mas não deve aceitar nenhuma outra entrada.

Você pode assumir que o colar de entrada é válido. Você não precisa lidar com o caso em que há um número ímpar de jóias de um determinado tipo ou o colar está vazio.

Resultado

Novamente, a saída pode estar em qualquer formato conveniente; por exemplo, uma lista de segmentos, uma lista de posições de corte, um dicionário com chaves representando os dois ladrões e os valores como listas de segmentos, etc. Os segmentos podem ser representados pelo índice inicial, final, lista de índices consecutivos, lista de jóias, seus comprimentos etc. Você pode usar 0- ou 1- indexação. Se a ordem não for significativa para o seu formato, sua saída poderá estar em qualquer ordem. Aqui está a saída acima em vários formatos diferentes:

list of segments: [[S],[S],[S,E,S,D,E,R,S],[R,E,S,S,S,D,R,E,E,R,E,D,E],[R,R,D,E,E,E]]
list of cuts:     [1,2,9,22]
list of lengths:  [1,1,7,13,6]
dictionary:       {'thief1' : [(R,R,D,E,E,E),(S),(S,E,S,D,E,R,S)], 'thief2' : [(S),(R,E,S,S,S,D,R,E,E,R,E,D,E)]}

Observe que a ordem é importante na lista de segmentos (segmentos alternados entre os ladrões) e na lista de comprimentos (para identificar os segmentos), mas não na lista de cortes ou no dicionário. Edit: Greg Martin apontou que estes não seriam resultados válidos, uma vez que uma divisão justa pode ser obtida em dois cortes

Casos de teste

[1,2,1,2,1,3,1,3,3,2,2,3] -> [[1,2,1],[2,1,3,1],[3,3,2],[2,3]]
[1,1,1,1,2,2,3,3,3,3,3,3] -> [[1,1],[1,1,2],[2,3,3,3],[3,3,3]]
[1,1,1,1,1,1,1,1,1,1,1,1] -> [[1,1,1,1,1,1],[1,1,1,1,1,1]]
[1,1,1,1,2,3,4,2,3,4,2,2] -> [[1,1],[1,1,2,3,4,2],[3,4,2,2]]

Notas

  1. As brechas padrão são proibidas.
  2. Isso é ; a resposta mais curta (em bytes) vence.
ngenisis
fonte
2
O colar é circular?
Dennis
11
@ Dennis Não, o colar é linear.
Ngenisis
11
Podemos usar a entrada como letras / tokens indicando os diferentes tipos de jóias, em vez de números inteiros?
Greg Martin
3
Se a ordem dos segmentos não for alterada, as partes alternam entre se A e B. Se assim for, incluir essas informações na saída é redundante. Podemos omitir a indicação se a resposta não mudar a ordem das jóias? Você tem alguns casos de teste?
Lucas
2
Para o seu exemplo [S,S,S,E,S,D,E,R,S,R,E,S,S,S,D,R,E,E,R,E,D,E,R,R,D,E,E,E], parece que a saída deve ser [[S,S,S,E,S,D,E,R],[S,R,E,S,S,S,D,R,E,E,R,E,D,E],[R,R,D,E,E,E]], pois isso tem menos cortes que [[S],[S],[S,E,S,D,E,R,S],[R,E,S,S,S,D,R,E,E,R,E,D,E],[R,R,D,E,E,E]]. Estou entendendo as especificações corretamente?
Greg Martin

Respostas:

3

Braquilog , 13 bytes

~c.ġ₂z₁Ċcᵐoᵛ∧

Experimente online!

Nota: o metapredicado é mais novo que esse desafio.

Explicação

~c.ġ₂z₁Ċcᵐoᵛ∧  Input is a list, say L = [1,2,2,2,1,2,3,3]
~c.            Output is a partition of the input: [[1,2,2],[2,1,2],[3],[3]]
  .ġ₂          Split the output into chunks of length 2: [[[1,2,2],[2,1,2]],[[3],[3]]]
     z₁        Zip (transpose) the chunks: [[[1,2,2],[3]],[[2,1,2],[3]]]
       Ċ       This is a 2-element list (forbid the trivial partition).
        cᵐ     Concatenate both: [[1,2,2,3],[2,1,2,3]]
          oᵛ   If you sort both lists, they are equal.
            ∧  Don't unify with the output.

As partições são enumeradas em ordem crescente do número de blocos, portanto o resultado terá o menor número possível de blocos.

Zgarb
fonte
3

Geléia , 18 bytes

s2ZFṢ$€E¬,L
ŒṖṖÇÞḢ

Experimente online!

Não eficiente - o exemplo tem 28 jóias que não funcionarão sem grandes recursos, pois o primeiro passo dessa implementação seria criar uma lista das 2 27 partições possíveis.

Retorna uma lista de listas - os segmentos na ordem para separá-los entre ladrões alternativos. (Na saída do TIO: quando uma lista possui apenas um único item, a impressão implícita não se preocupa com os colchetes. [])

Quão?

s2ZFṢ$€E¬,L - Link 1, get (isUnfair, Slices): A possible partition
s2          - split into slices of length 2 (any odd one on it's own at the end)
  Z         - transpose (first item is one thief's slices, second is the others)
     $€     - last two links as a monad for €ach
   F        -     flatten
    Ṣ       -     sort
       E    - equal? (theif1's jewels == theif2's jewels)
        ¬   - not
          L - length (number of slices in the partition)
         ,  - pair

ŒṖṖÇÞḢ - Main link: necklace
ŒṖ     - all partitions
  Ṗ    - pop, we must remove the rightmost one...
              because Link 1 will say it is fair, and it will have length 1!
              (a list of one thing has all entries equal)
    Þ  - sort by
   Ç   -     last link (1) as a monad
     Ḣ - head (get the first one, i.e. minimal isUnfair, then minimal length)
Jonathan Allan
fonte
3

Mathematica, 118 bytes

Quase venceu Jelly ... apenas 1 fora;)

SelectFirst[l_±c_:=Append[-#±Most@c,#2]&@@l~TakeDrop~Last@c;l_±{}:={l};i=#;(i±#)&/@Range@#2~Subsets~#3,Tr[Tr/@#]==0&]&

Função pura tomando três argumentos: o colar, como uma lista de fichas como {A, A, A, A, B, C, D, B, C, D, B, B}; o comprimento do colar; e o número de momentos distintos da joia. Ele retorna uma lista de sublistas no formulário {{A, A}, {-A, -A, -B, -C, -D, -B}, {C, D, B, B}}, onde os tokens sem sinais negativos vão para um ladrão e os tokens com sinais negativos vão para o outro ladrão. (Embora seja uma informação redundante, o algoritmo leva a essa representação e a remoção dos sinais negativos custaria vários bytes.)

Primeiro, temos que implementar uma função que pega uma lista e um conjunto de nlocais de corte e retorna a lista de n+1sublistas obtida cortando a lista de entrada nesses nlocais de corte; o operador de binário infix ±é usado para essa finalidade e definido recursivamente l_±c_:=Append[-#±Most@c,#2]&@@l~TakeDrop~Last@c;l_±{}:={l};. Devido ao sinal negativo logo após Append, o resultado é que os sublistas alternadamente possuem e não têm sinais negativos anexados a cada token.

Em seguida, geramos todos os conjuntos de locais de corte possíveis cujo comprimento é no máximo o número de tipos de joias, usando Range@#2~Subsets~#3e usado i=#;(i±#)&/@para aplicar o ±operador (com a lista de entrada de jóias) a cada um desses conjuntos de locais de corte.

Por fim, SelectFirst[...,Tr[Tr/@#]==0&]&escolhe a primeira das divisões de colar resultantes que é justa. Isso é feito somando literalmente todos os elementos em todas as sublistas; O Mathematica é inteligente o suficiente para cancelar as cópias positivas e negativas de cada token da maneira óbvia.

Greg Martin
fonte
3

Pitão, 16 bytes

hfqFSMsM.TcT2t./

Experimente on-line: Demonstration or Test Suite

Explicação:

hfqFSMsM.TcT2t./Q   implicit Q (=input) at the end
              ./Q   create all partitions of the input list 
                    (they are already sorted by number of cuts)
             t      remove the partition with zero splits
 f                  filter for partitions T, which satisfy:
          cT2          chop into pieces of length 2
        .T             transpose to get the pieces of each thieve
    SMsM               combine all pieces for each thieve and sort the results
  qF                   check if they got the same jewels
h                   print the first such partition
Jakube
fonte
1

05AB1E , 14 bytes

.œ¨ʒ2ôζε˜{}Ë}¤

Experimente online ou verifique todos os casos de teste .

Explicação:

                # All partitions of the (implicit) input
                  #  i.e. [2,3,2,1,3,1]
                  #   → [[[2],[3],[2],[1],[3],[1]],[[2],[3],[2],[1],[3,1]],
                  #      ...,[[2,3,2,1,3,1]]]
  ¨               # Remove the last one
   ʒ        }     # Filter this list by:
    2ô            # Split it into parts of 2
                  #  i.e. [[2,3],[2],[1],[3,1]] → [[[2,3],[2]],[[1],[3,1]]]
                  #  i.e. [[2,3,2],[1,3],[1]] → [[[2,3,2],[1,3]],[[1]]]
      ζ           # Swap rows and columns (using space as filler if necessary)
                  #  i.e. [[[2,3],[2]],[[1],[3,1]]] → [[[2,3],[1]],[[2],[3,1]]]
                  #  i.e. [[[2,3,2],[1,3]],[[1]]] → [[[2,3,2],[1]],[[1,3]," "]]
       ε  }       # Map each inner list to:
        ˜         # Flatten the list
                  #  i.e. [[2,3],[1]] → [2,3,1]
                  #  i.e. [[1,3]," "] → [1,3," "]
         {        # Sort the list
                  #  i.e. [2,3,1] → [1,2,3]
                  #  i.e. [1,3," "] → [1,3," "]
           Ë      # Check if both sorted lists are equal
                  # (if not, remove them from the partitions)
             ¤    # After filtering, take the last one as result (and output implicitly)
                  #  i.e. [[[2],[3,2],[1,3],[1]],[[2,3],[2],[1],[3,1]]]
                  #   → [[2,3],[2],[1],[3,1]]
Kevin Cruijssen
fonte