Досягнення щасливих чисел у репутації


21

Новий гольфіст, Джо, щойно зареєстрований на сайті. Він має 1 репутацію, але вирішив точно досягти всіх своїх щасливих чисел у репутації. Джо вірить у вищі сили, які допоможуть йому досягти своєї мети за допомогою мінімальної кількості (своїх чи інших) дій. Як новий користувач він також вважає, що можлива негативна репутація.

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

Деталі

  • Дії можуть змінити репутацію на наступні суми (усі дії доступні на кожному кроці незалежно від правил обміну пакетами):

    answer accepted:     +15
    answer voted up:     +10
    question voted up:    +5
    accepts answer:       +2
    votes down an answer: −1
    question voted down:  −2
    
  • Інші спеціальні зміни репутації не враховуються.

  • Вдалі цифри можуть бути негативними, і їх можна досягти в будь-якому порядку.
  • Ваше рішення має вирішити будь-який приклад тестового випадку за хвилину на моєму комп’ютері (я перевіряю лише тісні випадки. У мене ПК нижче середнього рівня).

Вхідні дані

  • Щасливі числа Джо як список цілих чисел у загальній формі вашої мови.

Вихідні дані

  • Кількість мінімальних дій, необхідних як одне ціле число.
  • Вихід може бути надрукований у stdout або повернутий як ціле число.

Приклади

Вхід => Вихід (Приклад репутації)

1                     => 0  (1)
3 2 1                 => 2  (1 -> 3 -> 2)
2 10 50               => 7  (1 -> 3 -> 2 -> 12 -> 10 -> 25 -> 40 -> 50)
10 11 15              => 3  (1 -> 11 -> 10 -> 15)
42 -5                 => 7  (1 -> -1 -> -3 -> -5 -> 10 -> 25 -> 40 -> 42)
-10                   => 6  (1 -> -1 -> -3 -> -5 -> -7 -> -9 -> -10)
15 -65                => 39
7 2015 25 99          => 142
-99 576 42 1111 12345 => 885

Це кодовий гольф, тому найкоротший запис виграє.

Відповіді:


1

C # - 501 байт

Оновлення 551 -> 501 байт

namespace System.Linq{class A {static void Main(){var i = c(new int[]{10,11,15});Console.WriteLine(i);Console.ReadLine();}private static int c(int[] a){var o=a.OrderBy(x => x).ToArray();int d=1,count=0;for(var i=0;i<a.Length;i++){if(o[i]==d)i++;int c=o[i],b=o.Length>=i+2?o[i+1]-o[i]:3;if(b<=2){i++;c=o[i];}while(d!=c){if(d>c){var e=d-c;if(e>1)d-=2;else d-=1;}else if(c>d){var e=c-d+2;if(e>14)d+=15;else if(e>9)d+=10;else if(e>4)d+=5;else if(e>2)d+=2;}count++;}if(b<=2){d-=b;count++;}}return count;}}}

Невикористаний код

namespace System.Linq {
    class Program {
        static void Main(){
            var i = CalculateNumbers(new int[]{10,11,15});
            Console.WriteLine(i);
            Console.ReadLine();
        }
        private static int CalculateNumbers(int[] numbers){
            var ordered = numbers.OrderBy(x => x).ToArray();
            int cur = 1, count = 0;
            for (var i = 0; i < numbers.Length; i++){
                if (ordered[i] == cur) i++;
                int val = ordered[i], next = ordered.Length >= i+2 ? ordered[i + 1] - ordered[i] : 3;
                if (next <= 2){
                    i++;
                    val = ordered[i];
                }
                while (cur != val){
                    if (cur > val){
                        var dif = cur - val;
                        if (dif > 1)
                            cur -= 2;
                        else
                            cur -= 1;
                    } else if (val > cur){
                        var dif = val - cur + 2;
                        if (dif > 14)
                            cur += 15;
                        else if (dif > 9)
                            cur += 10;
                        else if (dif > 4)
                            cur += 5;
                        else if (dif > 2)
                            cur += 2;
                    }
                    count++;
                }
                if (next <= 2){
                    cur -= next;
                    count++;
                }
            }
            return count;
        }
    }
}

16

Іржа, 929 923 символи

use std::io;use std::str::FromStr;static C:&'static [i32]=&[-2,-1,2,5,10,15];fn main(){let mut z=String::new();io::stdin().read_line(&mut z).unwrap();let n=(&z.trim()[..]).split(' ').map(|e|i32::from_str(e).unwrap()).collect::<Vec<i32>>();let l=*n.iter().min().unwrap();let x=n.iter().max().unwrap()-if l>1{1}else{l};let s=g(x as usize);println!("{}",p(1,n,&s));}fn g(x:usize)->Vec<i32>{let mut s=vec![std::i32::MAX-9;x];for c in C{if *c>0&&(*c as usize)<=x{s[(*c-1)as usize]=1;}}let mut i=1us;while i<x{let mut k=i+1;for c in C{if(i as i32)+*c<0{continue;}let j=((i as i32)+*c)as usize;if j<x&&s[j]>s[i]+1{s[j]=s[i]+1;if k>j{k=j;}}}i=k;}s}fn p(r:i32,n:Vec<i32>,s:&Vec<i32>)->i32{if n.len()==1{h(r,n[0],&s)}else{(0..n.len()).map(|i|{let mut m=n.clone();let q=m.remove(i);p(q,m,&s)+h(r,q,&s)}).min().unwrap()}}fn h(a:i32,b:i32,s:&Vec<i32>)->i32{if a==b{0}else if a>b{((a-b)as f32/2f32).ceil()as i32}else{s[(b-a-1)as usize]}}

Це було весело!


Коментар до реалізації

Тож я, очевидно, не надто задоволений розміром. Але Іржа в будь-якому випадку абсолютно жахлива. Виступ, однак, чудовий.

Код вирішує кожен тестовий випадок правильно за майже миттєвий проміжок часу, тому продуктивність, очевидно, не є проблемою. Для задоволення, ось набагато складніший тестовий випадок:

1234567 123456 12345 1234 123 777777 77777 7777 777

відповідь на 82317яку мою програму вдалося вирішити на моєму (середньодоступному) ноутбуці за 1,66 секунди (!), навіть за допомогою алгоритму рекурсивного гамільтонічного шляху грубої сили.

Спостереження

  • Спочатку ми повинні побудувати модифікований зважений графік, причому вузли мають кожне "щасливе" число, а ваги - скільки змін потрібно, щоб перейти від одного рівня репутації до іншого. Кожна пара вузлів повинна бути з'єднана двома краями, оскільки підйом вгору - це не те саме, що зниження цінності репутації (ви можете отримати +10, наприклад, але не -10).

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

    • Для отримання від більш високого до нижчого значення просто: просто візьміть, ceil((a - b) / 2)де aвище значення і bє нижче значення. Наш єдиний логічний варіант - використовувати -2 максимально, а потім -1 раз, якщо це необхідно.

    • Від низького до високого значення дещо складніше, оскільки використання максимально можливого значення не завжди є оптимальним (наприклад, від 0 до 9, оптимальне рішення - +10 -1). Однак це проблема динамічного програмування підручника, і для його вирішення достатньо простого ДП.

  • Після того, як ми підрахували мінімальні зміни від кожного числа до кожного іншого числа, ми, по суті, залишилися з невеликим варіантом TSP (проблема продавця подорожі). На щастя, існує досить мала кількість вузлів (максимум 5 у найскладнішому тестовому випадку), яких груба сила є достатньою для цього кроку.

Код без вольфів (сильно коментується)

use std::io;
use std::str::FromStr;

// all possible rep changes
static CHANGES: &'static [i32] = &[-2, -1, 2, 5, 10, 15];

fn main() {
    // read line of input, convert to i32 vec
    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
    let nums = (&input.trim()[..]).split(' ').map(|x| i32::from_str(x).unwrap())
        .collect::<Vec<i32>>();

    // we only need to generate as many additive solutions as max(nums) - min(nums)
    // but if one of our targets isn't 1, this will return a too-low value.
    // fortunately, this is easy to fix as a little hack
    let min = *nums.iter().min().unwrap();
    let count = nums.iter().max().unwrap() - if min > 1 { 1 } else { min };
    let solutions = generate_solutions(count as usize);

    // bruteforce!
    println!("{}", shortest_path(1, nums, &solutions));
}

fn generate_solutions(count: usize) -> Vec<i32> {
    let mut solutions = vec![std::i32::MAX - 9; count];

    // base cases
    for c in CHANGES {
        if *c > 0 && (*c as usize) <= count {
            solutions[(*c-1) as usize] = 1;
        }
    }

    // dynamic programming! \o/
    // ok so here's how the algorithm works.
    // we go through the array from start to finish, and update the array
    //   elements at i-2, i-1, i+2, i+5, ... if solutions[i]+1 is less than
    //   (the corresponding index to update)'s current value
    // however, note that we might also have to update a value at a lower index
    //   than i (-2 and -1)
    // in that case, we will have to go back that many spaces so we can be sure
    //   to update *everything*.
    // so for simplicity, we just set the new index to be the lowest changed
    //   value (and increment it if there were none changed).
    let mut i = 1us;  // (the minimum positive value in CHANGES) - 1 (ugly hardcoding)
    while i < count {
        let mut i2 = i+1;
        // update all rep-values reachable in 1 "change" from this rep-value,
        //   by setting them to (this value + 1), IF AND ONLY IF the current
        //   value is less optimal than the new value
        for c in CHANGES {
            if (i as i32) + *c < 0 { continue; }  // negative index = bad
            let idx = ((i as i32) + *c) as usize;  // the index to update
            if idx < count && solutions[idx] > solutions[i]+1 {
                // it's a better solution! :D
                solutions[idx] = solutions[i]+1;
                // if the index from which we'll start updating next is too low,
                //   we need to make sure the thing we just updated is going to,
                //   in turn, update other things from itself (tl;dr: DP)
                if i2 > idx { i2 = idx; }
            }
        }
        i = i2;  // update index (note that i2 is i+1 by default)
    }

    solutions
}

fn shortest_path(rep: i32, nums: Vec<i32>, solutions: &Vec<i32>) -> i32 {
    // mercifully, all the test cases are small enough so as to not require
    //   a full-blown optimized traveling salesman implementation
    // recursive brute force ftw! \o/
    if nums.len() == 1 { count_changes(rep, nums[0], &solutions) }  // base case
    else {
        // try going from 'rep' to each item in 'nums'
        (0..nums.len()).map(|i| {
            // grab the new rep value out of the vec...
            let mut nums2 = nums.clone();
            let new_rep = nums2.remove(i);
            // and map it to the shortest path if we use that value as our next target
            shortest_path(new_rep, nums2, &solutions) + count_changes(rep, new_rep, &solutions)
        }).min().unwrap()  // return the minimum-length path
    }
}

fn count_changes(start: i32, finish: i32, solutions: &Vec<i32>) -> i32 {
    // count the number of changes required to get from 'start' rep to 'finish' rep
    // obvious:
    if start == finish { 0 }
    // fairly intuitive (2f32 is just 2.0):
    else if start > finish { ((start - finish) as f32 / 2f32).ceil() as i32 }
    // use the pregenerated lookup table for these:
    else /* if finish > start */ { solutions[(finish - start - 1) as usize] }
}

1
Дивовижна відповідь! Мене цікавить Іржа, і пояснення насправді дуже корисні для навчання. І так само, як голова вгору, ви можете отримати підсвічування синтаксису <!-- language-all: lang-rust -->. ;)
Олексій А.

Я працюю над рішенням і побачив, що мінімальний обсяг змін від низької до великої ваги легко обчислити в O (1), використовуючи дуже невелику таблицю пошуку, як у цьому С-подібному псевдокоді floor((a-b)/15)+{0,2,1,2,2,1,3,2,2,2,1,3,2,2,2}[(a-b)%15]. Можливо, ваше рішення може отримати користь від цього.
Форс

2

Pyth - 43 42 байти

Використовує повністю грубу силу підходу з усіма перестановками та комбінаціями. Більше не дивлячись на гольф, тому що перекладається на Pyth. Перекладено.

K5tf.Em.Am}kmhs<dbUdQsm.pk.C[15yKK2_1_2)TZ

Це навіть повільніше, ніж версія python, оскільки я використовую фільтр замість циклу. Пояснення незабаром, тепер подивіться на код Python.

Спробуйте це онлайн .

from itertools import*
Q,Z=eval(input()),0
while True:
    if any(map(lambda d:all(map(lambda k:k in map(lambda b:sum(d[:b])+1,range(len(d))),Q)),chain.from_iterable(map(lambda k:permutations(k),combinations_with_replacement([15,10,5,2,-1,-2],Z))))):
        print(Z-1)
        break
    Z+=1

Роботи над малим, не давали йому закінчитись на великих.


Ви не прочитали код належним чином, але чи можете ви замінити 10, скажімо, y5для економії на пробілі?
Sp3000

@ Sp3000 це дозволить заощадити пробіл, але не будь-які символи в цілому. Але я думаю, що я можу врятувати K=5
чару, стиснувши

Зауважте, що ця відповідь не відповідає правилам, оскільки "Ваше рішення має вирішити будь-який приклад тестового випадку за хвилину". (Цитата
виділена

0

C ++ - 863 байти, невольф

Це працює досить швидко, в тому ж самому шарі, що і рішення, написане в Rust (приблизно в 6 разів швидше при включенні оптимізації). Я буду грати в гольф пізніше цього вечора (це вечір у Швеції, тобто).

#include <iostream>
#include <vector>
#include <string>
#include <sstream>

const int lookup[] = {0, 2, 1, 2, 2, 1, 3, 2, 2, 2, 1, 3, 2, 2, 2};

int distance(int start, int end) {
    return start > end
        ? (start - end + 1) / 2
        : (end - start) / 15 + lookup[(end - start) % 15];
}

int walk(int current, std::vector<int> points) {
    int min = 0;

    if (points.size() == 0) return 0;

    for (int i = 0; i < points.size(); i++) {
        std::vector<int> new_points = points;
        new_points.erase(new_points.begin() + i);

        int d = distance(current, points[i]) + walk(points[i], new_points);

        min = min && min < d ? min : d;
    }

    return min;
}

int main() {
    std::vector<int> points;

    std::string line;
    std::getline(std::cin, line);

    std::stringstream ss(line);
    int i;

    while (ss >> i)
        points.push_back(i);

    std::cout << walk(1, points) << std::endl;

    return 0;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.