Problema de división del collar

19

Antecedentes

Me inspiró el reciente video de 3Blue1Brown sobre el problema de división del collar (o como él lo llama, el problema del collar robado) y su relación con el teorema de Borsuk-Ulam .

En este problema, dos ladrones han robado un collar valioso que consta de varios tipos diferentes de joyas. Hay un número par de cada tipo de joya y los ladrones desean dividir cada tipo de joya de manera uniforme entre los dos. El problema es que deben hacerlo dividiendo el collar en un número de segmentos contiguos y distribuir los segmentos entre los dos.

Aquí hay un ejemplo con cuatro tipos de joya denotados S, E, D, y R(por zafiro, esmeralda, diamante y rubí, respectivamente). Digamos que el collar es el siguiente:

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

Hay 8zafiros, 10esmeraldas, 4diamantes y 6rubíes. Podemos dividir el collar de la siguiente manera:

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

Luego, si le damos los segmentos primero, tercero y quinto a un ladrón y el segundo y cuarto segmentos al otro ladrón, cada uno terminará con 4zafiros, 5esmeraldas, 2diamantes y 3rubíes:

[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, estos cortes ocurren en los índices [1,2,9,22].

Objetivo

Resulta que una división tan justa siempre se puede hacer con la mayoría de los ncortes, donde nestá el número de tipos de joyas. Su tarea es escribir un programa o función completa que tome un collar como entrada y produzca una división mínima (número mínimo de cortes).

Entrada

La entrada puede estar en cualquier formato conveniente. El collar debe ser una secuencia de joyas y nada más; Por ejemplo, una lista de enteros, diccionario con claves que representan los tipos de joyas y los valores que son listas de índices. Opcionalmente, puede incluir la longitud del collar o el número de tipos distintos de joyas, pero no debe tomar ninguna otra entrada.

Puede suponer que el collar de entrada es válido. No es necesario manejar el caso donde hay un número impar de joyas de un tipo dado o el collar está vacío.

Salida

Nuevamente, la salida puede estar en cualquier formato conveniente; por ejemplo, una lista de segmentos, una lista de posiciones de corte, un diccionario con claves que representan a los dos ladrones y los valores como listas de segmentos, etc. Los segmentos pueden representarse por su índice inicial, índice final, lista de índices consecutivos, lista de joyas, sus longitudes, etc. Puede usar 0- o 1- indexación. Si el orden no es significativo para su formato, entonces su salida puede estar en cualquier orden. Aquí está la salida anterior en varios 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)]}

Tenga en cuenta que el orden es importante en la lista de segmentos (los segmentos se alternan entre los ladrones) y la lista de longitudes (para identificar los segmentos), pero no en la lista de cortes o el diccionario. Editar: Greg Martin señaló que estos no serían resultados válidos ya que se puede obtener una división justa en dos cortes

Casos de prueba

[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. Las lagunas estándar están prohibidas.
  2. Este es el ; la respuesta más corta (en bytes) gana.
ngenisis
fuente
2
¿El collar es circular?
Dennis
1
@ Dennis No, el collar es lineal.
ngenisis
1
¿Podemos tomar la entrada como letras / fichas que indican los diferentes tipos de joyas, en lugar de enteros?
Greg Martin
3
Si no se cambia el orden de los segmentos, las piezas se alternan entre theif A y theif B. Como tal, incluir esa información en la salida es redundante. ¿Podemos omitir la indicación theif si la respuesta no cambia el orden de las joyas? ¿Tienes algunos casos de prueba?
Lucas
2
Para su ejemplo [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 la salida debería 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]], ya que tiene 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]]. ¿Estoy entendiendo la especificación correctamente?
Greg Martin

Respuestas:

3

Brachylog , 13 bytes

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

Pruébalo en línea!

Nota: el metapredicado es más nuevo que este desafío.

Explicación

~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.

Las particiones se enumeran en orden creciente de la cantidad de bloques, por lo que el resultado tendrá la menor cantidad de bloques posible.

Zgarb
fuente
3

Jalea , 18 bytes

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

Pruébalo en línea!

No es eficiente: el ejemplo tiene 28 joyas que no funcionarán sin grandes recursos, ya que el primer paso de esta implementación sería crear una lista de las 2 27 posibles particiones.

Devuelve una lista de listas: los segmentos en el orden para distribuirlos entre ladrones alternativos. (Re la salida TIO: cuando una lista solo tiene un único elemento, la impresión implícita no molesta con los corchetes [])

¿Cómo?

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
fuente
3

Mathematica, 118 bytes

Casi venció a Jelly ... solo 1 de descuento;)

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

Función pura tomando tres argumentos: el collar, como una lista de tokens como {A, A, A, A, B, C, D, B, C, D, B, B}; la longitud del collar; y el número de tiempos de joya distintos. Devuelve una lista de sublistas en el formulario{{A, A}, {-A, -A, -B, -C, -D, -B}, {C, D, B, B}} , donde las fichas sin signos negativos van a un ladrón y las fichas con signos negativos van al otro ladrón. (Si bien esta es información redundante, el algoritmo conduce a esta representación, y eliminar los signos negativos costaría varios bytes).

Primero tenemos que implementar una función que tome una lista y un conjunto de nlugares de corte y devuelva la lista de n+1sublistas obtenidas cortando la lista de entrada en esos nlugares de corte; el operador infijo binario ±se usa para este propósito y se define de forma recursiva a través de l_±c_:=Append[-#±Most@c,#2]&@@l~TakeDrop~Last@c;l_±{}:={l};. Debido al signo negativo justo despuésAppend , el resultado es que las sublistas alternativamente tienen y no tienen signos negativos adjuntos a cada token.

Luego generamos todos los conjuntos de lugares de corte posibles cuya longitud es como máximo el número de tipos de joyas, utilizando Range@#2~Subsets~#3y utilizamos i=#;(i±#)&/@para aplicar el ±operador (con la lista de entrada de joyas) a cada uno de estos conjuntos de lugares de corte a su vez.

Finalmente, SelectFirst[...,Tr[Tr/@#]==0&]&elige la primera de las divisiones de collar resultantes que es justa. Lo hace al agregar literalmente todos los elementos en todas las sublistas; Mathematica es lo suficientemente inteligente como para cancelar las copias positivas y negativas de cada token de la manera obvia.

Greg Martin
fuente
3

Pyth, 16 bytes

hfqFSMsM.TcT2t./

Pruébelo en línea: Demostración o conjunto de pruebas

Explicación:

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
fuente
1

05AB1E , 14 bytes

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

Pruébelo en línea o verifique todos los casos de prueba .

Explicación:

                # 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
fuente