Río digital (solución más corta y más rápida)


9

Esta es mi primera pregunta, así que espero que salga bien.

Antecedentes:

No es en los ríos en lo que podrías estar pensando. La pregunta gira en torno al concepto de ríos digitales. Un río digital es una secuencia de números donde el número siguiente nes nmás la suma de sus dígitos.

Explicación:

12345 es seguido por 12360 ya que 1 + 2 + 3 + 4 + 5 = 15 y 12345 + 15 da 12360. igualmente 145 es seguido por 155. Si el primer número de un río digital es Mlo llamaremos río M.

Por ejemplo: River 480 es la secuencia que comienza {480,492,507,519 ....}, y river 483 es ​​la secuencia que comienza {483,498,519, ....}. Las corrientes y ríos normales pueden encontrarse, y lo mismo es cierto para los ríos digitales. Esto sucede cuando dos ríos digitales comparten algunos de los mismos valores.

Ejemplo:

El río 480 se encuentra con el río 483 en 519. El río 480 se encuentra con el río 507 en 507 y nunca se encuentra con el río 481. Todos los ríos digitales eventualmente se encontrarán con el río 1, el río 3 o el río 9.

Escriba un programa que pueda determinar para un entero dado nel valor donde el río se nencuentra primero con uno de estos tres ríos.

Entrada

La entrada puede contener múltiples casos de prueba. Cada caso de prueba ocupa una línea separada y contiene un número entero n( 1 <= n <= 16384). Un caso de prueba con el valor 0for ntermina la entrada y este caso de prueba no debe procesarse.

Salida

Para cada caso de prueba en la entrada, primero envíe el número de caso de prueba (comenzando desde 1) como se muestra en la salida de muestra. Luego, en una salida de línea separada, la línea "primero se encuentra con el río x en y". Aquí y es el valor más bajo donde el río se nencuentra primero con el río x(x = 1 o 3 o 9). Si el río se nencuentra con el río xa ymás de un valor de x, arroje el valor más bajo. Imprima una línea en blanco entre dos casos de prueba consecutivos.

Caso de prueba

Entrada:

86
12345
0

Salida:

Case #1

first meets river 1 at 101

Case #2

first meets river 3 at 12423

Puntuación:

El algoritmo más rápido gana. En caso de empate. El que tenga un código más corto ganará.

Gracias a mbomb007 por señalar mi error.

ps: quiero tener la solución más rápida en lugar de la más pequeña. También tengo una solución mía que es lenta. Por eso mira aquí .

Nota:

Me va a utilizar esto para las pruebas de código. Y comprobación de rendimiento.


3
No estoy seguro de que puedas anotar de esa manera. ¿Qué pasa si el código de alguien es O (log (log n))? No puede cubrirlos a todos, por lo que debe decir que el algoritmo más rápido gana, pero en caso de empate, gana el código más corto y gana primero en caso de que ambos tengan la misma longitud.
mbomb007

3
No puedo encontrar nada sobre derechos de autor o usabilidad de los viejos desafíos ACM-ICPC, pero puedo encontrar este desafío en el sitio de archivo. ¿Está permitido usar aquí?
Geobits

1
Eso no tiene nada que ver con los derechos de autor. En caso de duda, lo más fácil es enviar un correo electrónico al propietario del sitio y preguntar.
Geobits

3
" Si el último dígito de un río digital es Mlo llamaremos ríoM " no tiene sentido por dos razones: en primer lugar, si un río es una secuencia infinita de números, entonces no tiene un último dígito; y en segundo lugar, en el siguiente párrafo, ríoM significa el río que comienza en el número M.
Peter Taylor

2
De la pregunta CR.SE vinculada, parece que un río es el número que comenzó en la serie, pero aquí está el último dígito. ¿Cual es correcta?
Celeo

Respuestas:


3

C, 320294 bytes

Compilar con -std = c99

#include<stdio.h>
int s(int i){for(int j=i;j;j/=10)i+=j%10;return i;}int main(){int c=0,i;while(scanf("%d",&i)){c++;if(!i)continue;int j,o[]={1,3,9},p[]={1,3,9};Q:for(j=0;j<3;j++){if(o[j]==i)goto D;else if(o[j]<i){o[j]=s(o[j]);goto Q;}}i=s(i);goto Q;D:printf("Case #%d\n\nfirst meets river %d at %d\n\n",c,p[j],o[j]);}}

Sin golf:

#include <stdio.h>

int s(int i)
{
    for(int j = i; j; j /= 10)
        i += j % 10;
    return i;
}

int main()
{
    int c = 0, i;
    while(scanf("%d", &i))
    {
        c++;
        if(!i)
            continue;
        int j,o[]={1,3,9},p[]={1,3,9};
        Q: for(j = 0; j < 3; j++)
        {
            if(o[j] == i)
                goto D;
            else if(o[j] < i)
            {
                o[j] = s(o[j]);
                goto Q;
            }
        }
        i = s(i);
        goto Q;
        D: printf("Case #%d\n\nfirst meets river %d at %d\n\n", c, p[j], o[j]);
    }
}

¡Pruébalo!

Esencialmente, los ríos "objetivo" se incrementan hasta que son mayores que el río contra el que estamos probando, y luego se aumenta el río de prueba. Esto se repite hasta que el río de prueba sea igual a otro río.

No estoy leyendo parámetros desde la línea de comandos en este programa, y ​​no estoy seguro de si se supone que debes hacerlo. Ahora puede pasar parámetros a STDIN. Puede terminar pasando una entrada no numérica.

También maldita sea, golpeado por media hora.


Estoy trabajando en casos de prueba por ahora. Solo 3 casos de prueba de entrada no serán muy adecuados.
Kishan Kumar

por favor, ¿le importaría recibir información de stdin?
Kishan Kumar

3

JavaScript (ES6)

Esta es una respuesta bastante rápida usando un lenguaje bastante lento. Realmente, el tiempo de ejecución no debería ser un problema al usar cualquier lenguaje con tablas hash. Todas mis pruebas de menos de 100 ms.

Método anónimo con la lista de casos de prueba como parámetro de entrada.

F=cases=>{
  var t0 = +new Date
  var result = 0
  var spots = []
  var top=[,1,3,,9]
  var rivers=[,1,3,1,9,1,3,1]
  cases.forEach((n,i)=>{
    var found = result = spots[n]
    for (;!found;)
    {
      found = top.some((v,i)=>{
        for(;spots[v] |= i, v<n; top[i] = v)
          [...v+''].forEach(d=>v-=-d)
        return result = v-n ? 0 : i;
      }) || (
        [...n+''].forEach(d=>n-=-d),
        result = spots[n]
      )
    }  
    console.log(`Case #${i+1}\nfirst meets river ${rivers[result]} at ${n}`)
  })  
  return 'Time (ms) ' + (new Date-t0)
}  

console.log(F([86, 12345, 123, 456, 789, 16384]))


1

Java 7, 519 505 bytes

import java.util.*;String c(int i){if(i<=0)return"";ArrayList<Long>r=f(1),s=f(3),t=f(9),x=f(i);String z="first meets river ";for(int j=0;j<r.size();j++){long u=r.get(j),v=s.get(j),w=t.get(j);if(x.contains(u))return z+1+" at "+u;if(x.contains(v))return z+3+" at "+v;if(x.contains(w))return z+9+" at "+w;}return"";}ArrayList f(long i){ArrayList<Long>l=new ArrayList();l.add(i);for(long j=0,x;j<9e4;j++){x=l.get(l.size()-1);for(char c:(x+"").toCharArray())x+=new Long(c+"");l.add(x);if(x>16383)return l;}return l;}

Sí, es largo, feo y, sin duda, puede cambiarse por completo para codificarlo más ... Estoy distraído y cansado, así que tal vez debería eliminarlo nuevamente ...
Fue un desafío bastante difícil para ser honesto. Pero al menos tienes tu primera respuesta ...;) (que podría ser incluso más larga que tu programa original de C ++ sin golf ... xD)

Sin golf y casos de prueba:

Pruébalo aquí.

import java.util.*;
class M{
  static String c(int i){
    if(i <= 0){
      return "";
    }
    ArrayList<Long> r = f(1),
                    s = f(3),
                    t = f(9),
                    x = f(i);
    String z = "first meets river ",
           y = " at ";
    for(int j = 0; j < r.size(); j++){
      long u = r.get(j),
           v = s.get(j),
           w = t.get(j);
      if(x.contains(u)){
        return z+1+y+u;
      }
      if(x.contains(v)){
        return z+3+y+v;
      }
      if(x.contains(w)){
        return z+9+y+w;
      }
    }
    return "";
  }

  static ArrayList f(long i){
    ArrayList<Long> l = new ArrayList();
    l.add(i);
    for(long j = 0, x; j < 9e4; j++){
      x = l.get(l.size() - 1);
      for(char c : (x + "").toCharArray()){
        x += new Long(c+"");
      }
      l.add(x);
      if(x > 16383){
        return l;
      }
    }
    return l;
  }

  public static void main(String[] a){
    System.out.println(c(86));
    System.out.println(c(12345));
    System.out.println(c(0));
  }
}

Salida:

first meets river 1 at 101
first meets river 3 at 12423
(empty output)

Compararé tu programa con el mío. También voy a publicar mi solución también. Por qué usar un lenguaje lento. Usa cualquier lenguaje rápido.
Kishan Kumar

Solo noté la etiqueta de algoritmo más rápido más adelante ... Siempre publico respuestas de código de golf Java 7 aquí ... Definitivamente no va a ganar ni en el más corto ni en el más rápido ... Por cierto, su rextester da errores cuando solo debería dar advertencias por falta de conversiones / inicializaciones de tipo. Funciona en ideone (y en Eclipse IDE).
Kevin Cruijssen

Okay. Déjame ver. rextester da tiempo de compilación y tiempo de ejecución. Así que lo usé
Kishan Kumar

Bueno, eso es un problema aquí. Buscaré otro compilador en línea que ofrezca el tiempo de compilación y el tiempo de ejecución
Kishan Kumar

@KishanKumar He agregado los moldes en mi código, lo que no debería afectar el tiempo afaik Aquí está el código rextester que funciona con el resultado: Compilation time: 0.62 sec, absolute running time: 0.14 sec, cpu time: 0.11 sec, memory peak: 22 Mb, absolute service time: 0,77 secpara mí localmente. Así que sí, es bastante lento ...
Kevin Cruijssen

1

Scala, 774 bytes

Violín: http://scalafiddle.net/console/4ec96ef90786e0f2d9f7b61b5ab0209b

No tengo ganas de jugar al golf. Encuentra una solución al problema planteado dentro de los 50 ms

El uso puede no ser exactamente lo que quieres:

scala river.scala

Ahora puede ingresar continuamente números seguidos de una entrada. Y finalice el programa con 0. El resultado se imprimirá tan pronto como presione enter.

io.Source.stdin.getLines.map(_.toInt)
  .takeWhile(_ != 0)
  .map(stream(_).takeWhile(_ < 16383))
  .zipWithIndex
  .map { cur =>
    Seq(1, 3, 9).map { i =>
      val s = stream(i).takeWhile(_ < 16383)
      (cur._2+1, i, s.intersect(cur._1).headOption)
    }
  }.foreach { opts =>
    val options = opts.filterNot(_._3.isEmpty)

    if(options.isEmpty) {
      println("No result")
    } else {
      val opt = options(0)
      println(s"Case #${opt._1}\n\nfirst meets ${opt._2} at ${opt._3.get}\n\n")
    }
  }

def stream(i:Int): Stream[Int] = {
  def sub: Int => Stream[Int] = {
    i => i #:: sub(a(i))
  }
  sub(i)
}

def a(i:Int): Int = i + i.toString.map{_.asDigit}.sum

No sé mucho sobre Scala. Entonces, ¿podría modificar el código, que según rextester.com/l/scala_online_compiler?
Kishan Kumar

Traté de ponerlo allí, pero se agotó el tiempo mientras compilaba.
AmazingDreams

ok @AmazingDreams
Kishan Kumar

@KishanKumar, incluso el valor predeterminado una vez, por lo que el sitio parece estar roto para scala
AmazingDreams

@KisthanKumar Use este scalafiddle.net/console/4ec96ef90786e0f2d9f7b61b5ab0209b aunque no admite stdin, así que tuve que cambiar algunas cosas menores.
AmazingDreams

1

C, 228283 300 bytes

Este es un mod del código de Yakov para aprovechar los patrones del río. Esto lo hace ~ 3 veces más rápido. Además, los enteros sin signo evitan la cltodpenalización en máquinas de 64 bits, por lo que son unos pocos bytes más largos pero fraccionalmente más rápidos.

#define sum(z) for(y=z;y;y/=10)z+=y%10;
n,x;main(){unsigned i,j,y;while(scanf("%d",&i)){if(i){j=x=1+!(i%3)*2+!(i%9)*6;do{while(j<i)sum(j)}while(j^i&&({sum(i)i;}));printf("Case #%u\n\nfirst meets river %u at %u\n\n",++n,x,i);}}}

Sin golf:

#define sum(z) for(y=z;y;y/=10)z+=y%10;
n, x;
main() {
    unsigned i, j, y;
    while(scanf("%d", &i)) {
        if(i){
            j = x = 1 + !(i%3)*2 + !(i%9)*6;
            do{
                while (j < i) sum(j)
            }
            while(j^i&&({sum(i)i;}));
            printf("Case #%u\n\nfirst meets river %u at %u\n\n", ++n, x, i);
        }
    }
}

Explicación:

j = x = 1 + !(i%3)*2 + !(i%9)*6;

Esto selecciona el río correcto. El río 1 se encuentra con cualquier otro río, por lo que usamos esto como el caso alternativo. Si 3 es el máximo divisor común del río de prueba, seleccionamos el río 3 ( 1 + !(i%3)*2). Si 9 es el máximo divisor común del río de prueba, anulamos los valores anteriores y seleccionamos el río 9.

¿Por qué funciona esto? El río 9 va 9, 18, 27, 36, etc. Este paso por un múltiplo de 9 cada vez, por lo que siempre será la ruta más corta a un río hermano. El río 3 pasará por un múltiplo de 3 cada vez: 3, 6, 12, 15, 21, etc. Si bien los ríos que son múltiplos de 9 también son múltiplos de 3, los elegimos primero como río 9, dejando solo el múltiplos de 3. El resto se encontrará primero con el río 1: 1, 2, 4, 8, 16, 23, 28, etc.

Una vez que hemos seleccionado nuestro río correcto, pisamos los dos ríos hasta que se encuentran.


1

Python 3, 144 bytes

r,a,b,c,i={int(input())},{1},{3},{9},1
while i:
  for x in r,a,b,c:t=max(x);x|={sum(int(c)for c in str(t))+t}
  if r&(a|b|c):i=print(*r&(a|b|c))

0

C

Muy simple, parece tan largo porque desenrollé los 3 ríos. Primero genera los 3 ríos hasta RIVER_LENGTH(que espero que sea lo suficientemente grande), y luego, para cada paso N, realiza una búsqueda binaria en los tres flujos para ver si está en alguno de ellos. Esto funciona porque las transmisiones ya están ordenadas, por lo que podemos hacer la verificación de contenido a log(n)tiempo.

#include <stdio.h>

#define RIVER_LENGTH 10000

int main() {
    int num_cases;
    scanf("%d", &num_cases);
    int cases[num_cases];
    int N;
    int s1[RIVER_LENGTH] = {1};
    int s3[RIVER_LENGTH] = {3};
    int s9[RIVER_LENGTH] = {9};
    int i;
    int temp;

    for (i = 1; i < RIVER_LENGTH; i++) {
        s1[i] = temp = s1[i-1];
        while (temp) {
            s1[i] += temp % 10;
            temp /= 10;
        }
    }

    for (i = 1; i < RIVER_LENGTH; i++) {
        s3[i] = temp = s3[i-1];
        while (temp) {
            s3[i] += temp % 10;
            temp /= 10;
        }
    }

    for (i = 1; i < RIVER_LENGTH; i++) {
        s9[i] = temp = s9[i-1];
        while (temp) {
            s9[i] += temp % 10;
            temp /= 10;
        }
    }

    int start;
    int end;
    int pivot;

    for (i=1; i <= num_cases; i++) {
        scanf("%d", &cases[i]);
    }

    for (i=1; i <= num_cases; i++) {
        printf("Case #%d\n\n", i);
        N = cases[i];

        while (1) {

            temp = N;
            while (temp) {
                N += temp % 10;
                temp /= 10;
            }

            start = 0;
            end = RIVER_LENGTH;
            pivot = 1;

            while (end != start && pivot != RIVER_LENGTH - 1) {
                pivot = start + ((end - start) >> 1);
                if (s1[pivot] == N) {
                    printf("first meets river 1 at %d\n\n", N);
                    goto case_done;
                } else if (N < s1[pivot]){
                    end = pivot;
                } else {
                    start = pivot+1;
                }
            }

            start = 0;
            end = RIVER_LENGTH;
            pivot = 1;

            while (end != start && pivot != RIVER_LENGTH - 1) {
                pivot = start + ((end - start) >> 1);
                if (s3[pivot] == N) {
                    printf("first meets river 3 at %d\n\n", N);
                    goto case_done;
                } else if (N < s3[pivot]){
                    end = pivot;
                } else {
                    start = pivot+1;
                }
            }

            start = 0;
            end = RIVER_LENGTH;
            pivot = 1;

            while (end != start && pivot != RIVER_LENGTH - 1) {
                pivot = start + ((end - start) >> 1);
                if (s9[pivot] == N) {
                    printf("first meets river 9 at %d\n\n", N);
                    goto case_done;
                } else if (N < s9[pivot]){
                    end = pivot;
                } else {
                    start = pivot+1;
                }
            }
        }

        case_done:;

    }
}

Primero se necesita un número para el número de casos, en lugar de usar 0para delimitar el final de las entradas, porque ya sabes, C. Esto es solo por conveniencia y realmente no afecta nada, así que espero que esté bien.


Este programa alcanza un límite de tiempo excedido en ideone en las entradas 86,12345,0
Kishan Kumar

ideone.com/mHCeef aquí está el enlace. Y da una salida de señal de muerte en rextester
Kishan Kumar

@KishanKumar Se necesita un número para el número de casos primero, en lugar de usar 0 para delimitar el final de las entradas, porque sabes, C. Esto es solo por conveniencia y realmente no afecta nada, así que espero que esté bien.
Maltysen

@KishanKumar prueba este en su lugar: rextester.com/XRJK89444
Maltysen

está bien. No hay problema. Pero tendré que escribir un script adicional para su programa. Como tengo que tomar el tiempo promedio de todo el rango de entrada.
Kishan Kumar
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.