Цифрова річка (найшвидше і найшвидше рішення)


9

Це моє перше питання, тому я сподіваюся, що це буде добре.

Фон:

Ви не можете думати про річки. Питання обертається навколо поняття цифрових річок. Цифрова річка являє собою послідовність чисел , де число наступне nв nплюс сума його цифр.

Пояснення:

12345 супроводжується 12360, оскільки 1 + 2 + 3 + 4 + 5 = 15, і тому 12345 + 15 дає 12360. аналогічно 145, а за 155. Якщо перший номер цифрової річки - Mми називаємо її рікою M.

Наприклад: Річка 480 - це послідовність, що починається {480,492,507,519 ....}, а річка 483 - послідовність, що починається {483,498,519, ....}. Звичайні потоки та річки можуть зустрічатися, і те саме стосується цифрових річок. Це відбувається, коли дві цифрові річки діляться деякими однаковими значеннями.

Приклад:

Річка 480 зустрічається з річкою 483 за 519. Річка 480 зустрічається з річкою 507 на 507 і ніколи не зустрічається з річкою 481. Кожна цифрова річка зрештою зустріне річку 1, річку 3 або річку 9.

Напишіть програму, яка може визначити для заданого цілого числа nзначення, де річка nвперше зустрічається з однією з цих трьох річок.

Вхідні дані

Вхід може містити кілька тестових випадків. Кожен тестовий випадок займає окремий рядок і містить ціле число n( 1 <= n <= 16384). Тестовий випадок зі значенням 0для nзавершує введення, і цей тестовий випадок не повинен оброблятися.

Вихідні дані

Для кожного тестового випадку на вході спочатку виведіть номер тестового випадку (починаючи з 1), як показано у вибірку вибірки. Потім на окремому рядку виведіть рядок "вперше зустрічається річка х у у". Тут y - найменше значення, де річка nвперше зустрічається з річкою x(x = 1 або 3 або 9). Якщо річка nвідповідає річці xпри yбільш ніж одному значенні x, виведіть найменше значення. Роздрукуйте порожній рядок між двома послідовними тестовими зразками.

Тестовий випадок

Вхід:

86
12345
0

Вихід:

Case #1

first meets river 1 at 101

Case #2

first meets river 3 at 12423

Оцінка:

Виграє найшвидший алгоритм. У разі краватки. Виграє той, хто має коротший код.

Дякую mbomb007 за вказівку на мою помилку.

ps: Я хочу мати швидке рішення, а не найменше. У мене також є рішення, яке є повільним. Для цього дивіться тут .

Примітка:

Я буду використовувати це для тестування коду. І перевірка працездатності.


3
Я не впевнений, що можна забити так. Що робити, якщо чийсь код є O (log (log n))? Ви не можете покрити їх усіма, тому вам потрібно просто сказати, що найшвидший алгоритм виграє, але у випадку зрівноваження виграє найкоротший код, і перший опублікований виграш, якщо обидва мають однакову довжину.
mbomb007

3
Я не можу знайти нічого щодо авторських прав або зручності використання старих проблем ACM-ICPC, але я можу знайти цю проблему на сайті архіву. Чи дозволено тут використовувати?
Геобіт

1
Це не має нічого спільного з авторським правом. Якщо ви сумніваєтеся, найпростіше, як правило, надіслати електронною поштою власника (-ів) сайту і запитати.
Геобіт

3
" Якщо остання цифра цифрової річки Mбудемо називати її рікоюM " не має сенсу з двох причин: по-перше, якщо річка є нескінченною послідовністю чисел, то вона не має останньої цифри; по-друге, у наступному абзаці річкаM означає річку, що починається в кількості M.
Пітер Тейлор

2
Із пов'язаного запитання CR.SE, здається, що річка, з якої цифри розпочато в серії, але ось остання цифра. Що правильно?
Селео

Відповіді:


3

C, 320 294 байт

Компілюйте з -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]);}}

Безголівки:

#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]);
    }
}

Спробуй!

По суті, "цільові" річки збільшуються до тих пір, поки вони не перевищують річку, яку ми випробовуємо, а згодом тест-річку не збільшують. Це повторюється, поки тестова річка не дорівнює річці якоїсь іншої.

Я не читаю параметри з командного рядка в цій програмі, і не впевнений, чи потрібно це робити. Тепер ви можете передати параметри STDIN. Ви можете закінчити, передавши нечисловий ввід.

Також чорт, побили півгодини.


Я зараз працюю над тестовими справами. Тільки 3 вхідні тестові випадки не будуть дуже придатними.
Кішань Кумар

Будь ласка, ви б заперечили взяти внесок від stdin.
Кішань Кумар

3

JavaScript (ES6)

Це досить швидка відповідь, використовуючи досить повільну мову. Дійсно, виконання часу не повинно бути проблемою при використанні будь-якої мови з хеш-таблицями. Всі мої тести менше 100 мс.

Анонімний метод із переліком тестового випадку як вхідний параметр.

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 байт

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;}

Так, це довго, потворно і, без сумніву, може бути повністю змінено на код-гольф, це більше .. Я і розгублений, і втомлений, тому, можливо, я повинен просто видалити його знову ..
Це було досить важким завданням, щоб бути чесним. . Але, принаймні, у вас є перша відповідь ..;) (яка може бути навіть довшою, ніж ваша оригінальна програма для безготівкового C ++ .. xD)

Невикористані та тестові справи:

Спробуйте тут.

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));
  }
}

Вихід:

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

Я порівняю вашу програму з моєю. Я також збираюся розмістити своє рішення також. Навіщо використовувати повільну мову. Використовуйте будь-яку швидку мову.
Кішан Кумар

Пізніше я помітив лише тег найшвидшого алгоритму .. Я завжди розміщую тут відповіді з кодом-гольф Java 7. Однозначно не вигравати ні в найкоротшій, ні в найшвидшій мірі .. До речі, ваш рекстестер робить помилки, коли повинен лише давати попередження за відсутності кастів / тип-ініціалізує .. Він працює на ideone (і в Eclipse IDE).
Кевін Круїйсен

добре. дозвольте мені побачити. rextester дає час компіляції та час виконання. Тож я використав це
Кішан Кумар

ну ось проблема тут. Я буду шукати інший онлайн-компілятор, який дає час збирання та час виконання
Kishan Kumar

@KishanKumar Я додав кадри в свій код, які не повинні впливати на час afaik Ось робочий код рекстестера з результатом: 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 secдля мене локально. Так що так, це досить повільно ..
Кевін Круїйсен

1

Scala, 774 байт

Fiddle: http://scalafiddle.net/console/4ec96ef90786e0f2d9f7b61b5ab0209b

Мені не здається, що це гольф. Він знаходить рішення поставленої проблеми протягом 50мс

Використання може бути не таким, яким ви хочете:

scala river.scala

Тепер ви можете постійно вводити цифри, за якими слід вводити. І завершіть програму на 0. Результат буде надруковано, як тільки ви натиснете клавішу 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

Я мало знаю про Скалу. Тож будь ласка, ви можете змінити код, який буде відповідно до rextester.com/l/scala_online_compiler
Kishan Kumar

Я спробував помістити його там, але він вичерпався під час компіляції.
AmazingDreams

ok @AmazingDreams
Кумар

@KishanKumar навіть один раз за замовчуванням виходив, тому сайт, здається, зламаний для масштабу
AmazingDreams

@KisthanKumar Використовуйте цей scalafiddle.net/console/4ec96ef90786e0f2d9f7b61b5ab0209b він не підтримує stdin, хоча мені довелося змінити деякі незначні речі.
AmazingDreams

1

C, 228 283 300 байт

Це мод коду Якова, щоб скористатися візерунками річок. Це робить ~ 3 рази швидшим. Крім того, непідписані цілі числа уникають cltodштрафу на 64-бітних машинах, тому це на кілька байт довше, але частково швидше.

#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);}}}

Безголівки:

#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);
        }
    }
}

Пояснення:

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

Це вибирає правильну річку. Річка 1 зустрічається з кожною другою річкою, тому ми використовуємо це як резервний випадок. Якщо 3 є найбільшим спільним дільником тестової річки, виберемо річку 3 ( 1 + !(i%3)*2). Якщо 9 є найбільшим спільним дільником тестової річки, ми переосмислюємо попередні значення і вибираємо річку 9.

Чому це працює? Річка 9 проходить 9, 18, 27, 36 і т. Д. Цей крок по кратному 9 кожен раз, таким чином, це завжди буде найкоротшим маршрутом до сестринської річки. Річка 3 переходитиме на кратну 3 кожного разу: 3, 6, 12, 15, 21 і т. Д. Хоча річки, кратні 9, також кратні 3, ми вибираємо їх спочатку як річку 9, залишаючи лише кратні по 3. Решта зустрінеться першою річкою 1: 1, 2, 4, 8, 16, 23, 28 тощо.

Як тільки ми вибрали нашу правильну річку, ми крокуємо по дві річки, поки вони не зустрінуться.


1

Пітон 3, 144 байти

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

С

Дуже просто, це виглядає так довго, тому що я розкрутив усі 3 річки. Спочатку він генерує 3 річки до RIVER_LENGTH(що, я сподіваюся, є досить великим), а потім для кожного кроку на Nньому робиться двійковий пошук на всіх трьох потоках, щоб побачити, чи є він у будь-якому з них. Це працює, тому що потоки вже відсортовані, тому ми можемо зробити перевірку вмісту log(n)вчасно.

#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:;

    }
}

Спочатку потрібне число для кількості справ, замість того, щоб використовувати 0для обмеження кінця входів, тому що ви знаєте, C. Це просто для зручності і насправді нічого не впливає, тому я сподіваюся, що все добре.


Ця програма перевищує ліміт часу в ideone на введення 86,12345,0
Kishan Kumar

ideone.com/mHCeef тут посилання. І це дає вихідний сигнал вбивства на рекстестер
Кішан Кумар

@KishanKumar Спочатку потрібне число для кількості випадків, замість того, щоб використовувати 0 для розмежування кінця входів, тому що ви знаєте, C. Це просто для зручності і насправді нічого не впливає, тому я сподіваюся, що це добре.
Малтісен

@KishanKumar спробуйте це замість цього: rextester.com/XRJK89444
Малтісен

нічого страшного. Нема проблем. Але мені доведеться написати додатковий сценарій для вашої програми. Як я повинен взяти середній час для всіх вхідних діапазонів.
Кішань Кумар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.