¿Cuánto tiempo lleva pintar un palo?

12

(Basado en este problema de Math.SE , que también proporciona algunos gráficos)

Tengo un palo que se parece a esto:

ingrese la descripción de la imagen aquí

Quiero que se vea así:

ingrese la descripción de la imagen aquí

Sin embargo, no soy una pintora experta, así que antes de comenzar un proyecto de bricolaje tan ambicioso, quiero asegurarme de que no me sobrepase.

Su programa debería decirme cuántos pasos implican pintar este palo. Cada paso implica pintar un área continua con un color sólido, que cubre capas anteriores de pintura. Para el ejemplo anterior, podría pintar la mitad izquierda azul, la mitad derecha roja y luego las dos áreas verdes separadas para un total de 4 pasos (el verde no se pinta continuamente).

ingrese la descripción de la imagen aquí

Aquí está en ASCII:

------
bbb---
bbbrrr
bgbrrr
bgbrgr

Hay un par de formas diferentes de pintar este palo y terminar con el mismo resultado. Sin embargo, solo me interesa el tiempo estimado, que son cuatro pasos.

Objetivo

Su programa debe generar el número mínimo de pasos necesarios para pintar un palo con un esquema de color dado. El esquema de pintura tendrá la forma de una cadena de caracteres, mientras que la salida será un número. Este es el código de golf. El programa más corto gana.

Entrada

Su programa recibirá el esquema de color para un palo en forma de una cadena de letras. Cada letra única (mayúsculas y minúsculas) representa un color único.

YRYGR

grG

GyRyGyG

pbgbrgrp

hyghgy

Salida

Estos números son el menor número de pasos necesarios para pintar los palos.

4

3

4

5

4

Explicaciones

Así es como llegué a los números anteriores. Su programa no necesita generar esto:

 -----
 YYY--
 YRY--
 YRYG-
 YRYGR

 ---
 g--
 gr-
 grG

 -------
 GGGGGGG
 GyyyGGG
 GyRyGGG
 GyRyGyG

 --------
 pppppppp
 pbbbpppp
 pbbbrrrp
 pbgbrrrp
 pbgbrgrp

 ------
 -yyyyy
 -ygggy
 hygggy
 hyghgy

Editar: agregaré más casos de prueba si resultan ser casos de prueba más difíciles.

PhiNotPi
fuente
Esto me recuerda a stackoverflow.com/q/10364248/785745 , que es similar, pero en 2D.
Kendall Frey

Respuestas:

3

GolfScript, 82 72 67 caracteres

.,n*{:c,,{[).2$1<*\c>+1$.,,{).2$<\3$<=},,.@>@@>]}%\;{~S)}%$0+0=}:S~

Razonablemente rápido para un programa GolfScript, ejemplos:

> hyghgy
4

> pbgbrgrp
5

El algoritmo utilizado funciona a través de los colores de izquierda a derecha de forma recursiva, de acuerdo con las siguientes afirmaciones:

  • Ignora todas las partes de la izquierda que ya tienen el color requerido. Pintarlos de nuevo no proporcionará una mejor respuesta.
  • Si el palo completo ya tiene el color deseado, devuelve 0 pasos como resultado.
  • De lo contrario, tome el color objetivo de la parte más a la izquierda (es decir, la primera que no esté en el color deseado).

    • Pinte 1 parte con el color de destino y recurse.
    • Pinte 2 partes con este color y recurse.

    ...

    • Pinte todo el palo restante con este color y recurse.

    Tome el mínimo de todos esos números y agregue 1 (para el paso actual). Devuelva esto como el número óptimo de pasos.

Este algoritmo funciona, porque la parte más a la izquierda tiene que pintarse de una vez de todos modos, entonces, ¿por qué no hacerlo de inmediato?

.,n*         # prepare the initial situation (target stick, unpainted stick)
             # used n instead of '-' for the unpainted parts, because it is shorter
{            # Define the operator S which does t c S -> n
             #   - t: the desired target colors
             #   - c: the stick as it is colored currently
             #   - n: minimum number of steps required to go from c to t
  :c         # Assign the current configuration to the variable c
  ,,{[       # Map the list [0 1 2 .. L-1]
             # We will build a list of all possible configurations if we paint
             # the stick with the 0th part's color (either 1 or 2 or 3 or ... parts)
    ).       # Increase the number, i.e. we paint 1, 2, 3, ... L parts, copy
    2$1<     # Take the first color of the target configuration
    *        # ...paint as many parts
    \c>+     # ...and keep the rest as with the current stick
    1$       # take the target configuration
             # Top of stack now e.g. reads
             #    h----- hyghgy (for i=0)
             #    hh---- hyghgy (for i=1)
             # Next, we strip the common leading characters from both strings:
    .,,      # Make list [0 1 2 ... L-1]
    {        # Filter {}, all the numbers for which the first j+1 parts are the same
      ).     # incr j
      2$<    # take leftmost j parts of stick A
      \3$<   # take leftmost j parts of stick B
      =      # are they equal?
    },,      # The length of the result is the number of common parts.
    .@>      # cut parts from A
    @@>      # cut parts from B
  ]}%
  \;         # Remove the target from the stack (not needed anymore)               
             # We now have a list of possible paintings where 1, 2, ..., L parts were
             # painted from the left with the same color.
             # All configurations were reduced by the common parts (they won't be
             # painted over anyways)
  {~S)}%     # Call the method S recursively on the previously prepared configurations
  $0+0=      # $0= -> sort and take first, i.e. take the minimum, 0+ is a workaround
             # if the list was empty (i.e. the operator was called with a stick of length 0).
}:S
~            # Execute S
Howard
fuente
+1 para el algoritmo de "color más a la izquierda incorrecto". Sin embargo, parece ser n!pasos;) (pero tal vez esta es la verdadera complejidad, no sé).
yo '
2

JavaScript: 187 bytes

¡Asumiendo que se nos permita tener solo la entrada y salida de una función (por favor)!

function p(w){var j,l,s=-1,i,m=i=w.length;if(m<2)return m;for(;i--;){l = 1;for(var k=-1;(j=k)!=m;){if((k=w.indexOf(w[i],++j))==-1)k=m;l+=p(w.substring(j,k));}if(s==-1||l<s)s=l;}return s;}

157 Bytes haciendo una optimización fea adicional (que es parte del punto, y me pareció increíblemente divertido):

function p(w){var j,l,s=-1,i,m=i=w.length;if(m<2)return m;for(;i--;s<0|l<s?s=l:1)for(l=1,j=0;~j;)l+=p(w.substring(j,(j=w.indexOf(w[i],j))<0?m:j++));return s}

135 Bytes Me di cuenta de que la longitud de la entrada es un límite superior y ahora no necesito manejar casos triviales por separado.

function p(w){var j,l,s,i,m=s=i=w.length;for(;i--;l<s?s=l:1)for(l=1,j=0;~j;)l+=p(w.substring(j,~(j=w.indexOf(w[i],j))?j++:m));return s}

Casos de prueba:

p("YRYGR")
4
p("grG")
3
p("GyRyGyG")
4
p("pbgbrgrp")
5
p("hyghgy")
4

Versión ampliada:

function paint(word) {
    var smallest = -1;
    if(word.length < 2) return word.length;
    for(var i = word.length; i-->0;) {
        var length = 1;
        for(var left, right = -1;(left = right) != m;) {
            if((right = word.indexOf(word[i],++left)) == -1) right = word.length;
            if(left != right) length += paint(word.substring(left , right));
        }
        if(smallest == -1 || length < smallest) smallest = l;
    }
    return smallest;
}

Para cada carácter de la entrada, calcule la longitud del patrón si primero pintamos ese color. Esto se hace fingiendo que pintamos ese color y luego haciendo subsecciones de los bits que no son de ese color, y recurriendo recursivamente al método de pintura en ellos. Se devuelve el camino más corto.

Ejemplo ( YRYGR):

Inicialmente, inténtalo R. Esto nos da los subgrupos Yy YG. Yestá pintado trivialmente de una vez.

Para YG: Intentar G, Yes trivial, longitud 2. Intenta Y, Ges trivial, la longitud 2. YGes, por lo tanto, longitud 2.

Pintar Rprimero por lo tanto nos da1 + 1 + 2 = 4

Entonces inténtalo G. Esto nos da los subgrupos YRYy R. Res trivial

Para YRY:

Probar Y: Res trivial, longitud 2. Prueba R: Yy Yson los dos grupos, longitud 3.

YRYes la longitud 2.

Pintar Gprimero da1 + 1 + 2 = 4

Entonces inténtalo Y. Esto le da a los subgrupos Ry GR. Rtrivial, GRes longitud 2. Yes longitud4

Esta implementación luego verificaría R e Y nuevamente para reducir la longitud del código. El resultado para YRYGRes por lo tanto 4.

meiamsome
fuente
Creo que su versión ampliada perdió la var en malguna parte.
Hasturkun
Desafortunadamente, también su versión no da el resultado correcto para la entrada "abcacba".
Howard
@Hasturkun mfue solo una abreviatura de word.length:) @Howard Tienes razón, tendré que repensar esto.
meiamsome
1

Python, 149 caracteres

D=lambda s:0 if s=='?'*len(s)else min(1+D(s[:i]+'?'*(j-i)+s[j:])for i in range(len(s))for j in range(i+1,len(s)+1)if set(s[i:j])-set('?')==set(s[i]))

Yo uso ?para marcar un área que puede ser de cualquier color. Dselecciona una región contigua del palo que contiene solo un color (más tal vez algunos ?s), colorea esa región al final, reemplaza esa región con ?sy vuelve a buscar todos los pasos anteriores.

Tiempo de ejecución exponencial. Apenas lo suficientemente rápido como para hacer los ejemplos en un tiempo razonable (unos minutos). Apuesto a que con la memorización podría ser mucho más rápido.

Keith Randall
fuente
1

Python 3 - 122

def r(s,a):c=s[0];z=c in a;return s[1:]and min([1+r(s[1:],a+c)]+[r(s[1:],a[:a.rfind(c)+1])]*z)or(1-z)
print(r(input(),''))

Parece funcionar, pero todavía no estoy 100% seguro de que este método siempre encuentre el número mínimo de pasos.

grc
fuente
1

CoffeeScript - 183 247 224 215 207

x=(s,c='')->return 0if !s[0]?;return x s[1..],c if s[0]is c;l=s.length;return x s[1...l],c if s[l-1]is c;i=s.lastIndexOf s[0];1+(x s[1...i],s[0])+x s[i+1..],c
y=(z)->z=z.split "";Math.min (x z),x z.reverse()

Demo en JSFiddle.net

Versión sin golf que incluye código de depuración y comentarios:

paintSubstring = (substring, color = '####') ->
  console.log 'Substring and color', substring, color, substring[0]
  # no need to color here
  if !substring[0]?
    return 0
  # no need to recolor the first part
  if substring[0] is color
    return paintSubstring (substring[1..]), color
  l = substring.length
  # no need to recolor the last part
  if substring[l-1] is color
    return paintSubstring substring[0...l], color
  # cover as much as possible
  index = substring.lastIndexOf substring[0]
  part1 = substring[1...index]
  part2 = substring[index+1..]
  console.log 'Part 1:', part1
  console.log 'Part 2:', part2
  # color the rest of the first half
  p1 = paintSubstring part1, substring[0]
  # color the rest of the second half, note that the color did not change!
  p2 = paintSubstring part2, color
  # sum up the cost of the substick + this color action
  return p1+p2+1
paintSubstringX=(x)->Math.min (paintSubstring x.split("")), paintSubstring x.split("").reverse()
console.clear()
input = """YRYGR
grG
GyRyGyG
pbgbrgrp
aaaaaaaa
hyghgy""".split /\n/
for stick in input
  console.log paintSubstringX stick
TimWolla
fuente
Parece que devuelve un resultado incorrecto para hyghgy. Dice 5 pero debería ser 4. (sin embargo, devuelve el resultado correcto de 4 para hyghgyh).
PhiNotPi
@PhiNotPi Dios, esto me hizo retroceder más de 60 caracteres :( Tratando de volver a implementar esto en otro idioma, después de tomar una siesta.
TimWolla
0

Haskell, 143 caracteres

f s=g(r n ' ')0where{r=replicate;n=length s;g p l=minimum$n:[0|p==s]++[1+g(take i p++r(j-i)(s!!i)++drop j p)(l+1)|l<n,i<-[0..n-1],j<-[i+1..n]]}

Esto intenta todas las reescrituras posibles hasta la longitud de la cadena, y continúa hasta que encuentra una que construya el patrón de entrada. No hace falta decir, tiempo exponencial (y algo más).

Seudónimo
fuente
0

Una primera búsqueda de amplitud simple. No pone nada en la cola que ya se haya visto. Funciona en menos de un segundo para todos los ejemplos, pero 'pbgbrgrp', que en realidad lleva un minuto completo :(

Ahora que tengo algo que funciona , estaré trabajando para encontrar algo más rápido y más corto.

Python - 300 296

def f(c):
 k=len(c);q=['0'*99];m={};m[q[0]]=1
 while q:
  u=q.pop(0)
  for x in ['0'*j+h*i+'0'*(k-j-i) for h in set(c) for j in range(1+k) for i in range(1,1+k-j)]:
   n=''.join([r if r!='0' else l for l,r in zip(u,x)])
   if n == c:
     return m[u]
   if not n in m:
    m[n]=m[u]+1;q.append(n)
TrevorM
fuente
¿Puedes comprobar la sangría? Parece estar roto
Howard
@Howard arreglado. No tengo idea de lo que estaba pensando anoche.
TrevorM
0

Haskell, 118 86 caracteres

p""=0
p(a:s)=1+minimum(map(sum.map p.words)$sequence$map(a%)s)
a%b|a==b=b:" "|1<3=[b]

Pruebas de funcionamiento:

λ: p "YRYGR"
4

λ: p "grG"
3

λ: p "GyRyGyG"
4

λ: p "pbgbrgrp"
5

λ: p "hyghgy"
4

λ: p "abcacba"
4

¡Este método ni siquiera es tan ineficiente!

MtnViewMark
fuente