Raggiungere i propri numeri fortunati in reputazione


21

Un nuovo golfista di codice, Joe, si è appena registrato sul sito. Ha 1 reputazione ma è determinato a raggiungere esattamente tutti i suoi numeri fortunati in reputazione. Joe crede in poteri superiori che lo aiuteranno a raggiungere il suo obiettivo con una quantità minima di (sue o altre) azioni. Come nuovo utente crede anche che la reputazione negativa sia possibile.

Dovresti scrivere un programma o una funzione che aiuti Joe a calcolare quante azioni dovrebbe aspettarsi.

Dettagli

  • Le azioni possono cambiare la reputazione secondo i seguenti importi (tutte le azioni sono disponibili in ogni fase indipendentemente dalle regole di scambio di stack):

    answer accepted:     +15
    answer voted up:     +10
    question voted up:    +5
    accepts answer:       +2
    votes down an answer: −1
    question voted down:  −2
    
  • Altre modifiche speciali alla reputazione vengono ignorate.

  • I numeri fortunati possono essere negativi e possono essere raggiunti in qualsiasi ordine.
  • La tua soluzione deve risolvere qualsiasi caso di test di esempio in meno di un minuto sul mio computer (testerò solo casi chiusi. Ho un PC sotto la media.).

Ingresso

  • I numeri fortunati di Joe come un elenco di numeri interi in una forma comune della tua lingua.

Produzione

  • Il numero di azioni minime necessarie come singolo numero intero.
  • L'output può essere stampato su stdout o restituito come numero intero.

Esempi

Input => Output (esempio stati di reputazione)

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

Questo è code-golf, quindi vince l'ingresso più breve.

Risposte:


1

C # - 501 byte

Aggiornamento 551 -> 501 byte

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

Codice Ungolfed

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

Ruggine, 929 923 caratteri

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

È stato divertente!


Commento sull'attuazione

Quindi ovviamente non sono molto contento delle dimensioni. Ma Rust è assolutamente terribile nel giocare a golf. La performance, tuttavia, è meravigliosa.

Il codice risolve correttamente ciascuno dei casi di test in un tempo quasi istantaneo, quindi le prestazioni non sono ovviamente un problema. Per divertimento, ecco un test case molto più difficile:

1234567 123456 12345 1234 123 777777 77777 7777 777

per cui la risposta è 82317, che il mio programma è stato in grado di risolvere sul mio laptop (a medio rendimento) in 1,66 secondi (!), anche con l'algoritmo ricorsivo del percorso Hamiltoniano a forza bruta.

osservazioni

  • Per prima cosa dovremmo costruire un grafico ponderato modificato, con i nodi che sono ogni numero "fortunato" e i pesi sono quanti cambiamenti ci vogliono per passare da un livello di reputazione a un altro. Ogni coppia di nodi deve essere connessa da due bordi, poiché salire non equivale a scendere in valore di reputazione (puoi ottenere +10, ad esempio, ma non -10).

  • Ora dobbiamo capire come trovare la quantità minima di modifiche da un valore rep all'altro.

    • Per passare da un valore più alto a un valore più basso, è semplice: basta prendere ceil((a - b) / 2)dove si atrova il valore più alto e bil valore più basso. La nostra unica opzione logica è usare -2 il più possibile e quindi -1 una volta, se necessario.

    • Un valore da basso a alto è un po 'più complicato, poiché l'utilizzo del valore più grande possibile non è sempre ottimale (es. Per 0-9, la soluzione ottimale è +10 -1). Tuttavia, si tratta di un problema di programmazione dinamica da manuale e una semplice DP è sufficiente per risolverlo.

  • Una volta che abbiamo calcolato le modifiche minime da ciascun numero a ogni altro numero, essenzialmente ci rimane una leggera variante di TSP (problema del venditore ambulante). Fortunatamente, esiste un numero di nodi abbastanza piccolo (un massimo di 5 nel caso di test più difficile) che la forza bruta è sufficiente per questo passaggio.

Codice non golfato (fortemente commentato)

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
Risposta fantastica! Sono interessato a Rust e la spiegazione è in realtà molto utile per l'apprendimento. E proprio come un avvertimento, puoi ottenere l'evidenziazione della sintassi con <!-- language-all: lang-rust -->. ;)
Alex A.

Sto lavorando su una soluzione, e visto che la quantità minima di modifiche sul basso a alto peso può essere facilmente calcolato in O (1) utilizzando una piccola ricerca-tavolo, come in questo C-come pseudo-codice floor((a-b)/15)+{0,2,1,2,2,1,3,2,2,2,1,3,2,2,2}[(a-b)%15]. La tua soluzione potrebbe probabilmente trarne vantaggio.
Fors,

2

Pyth - 43 42 byte

Utilizza un approccio di forza completamente bruta con tutte le permutazioni e combinazioni. Non guardare più al golf perché si tradurrà in Pyth. Tradotto.

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

Questo è persino più lento della versione di Python perché uso il filtro anziché un ciclo while. Spiegazione in arrivo, ora guarda il codice Python.

Provalo qui online .

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

Funziona su quelli piccoli, non ha funzionato fino a completamento su quelli grandi.


Non hai letto correttamente il codice ma puoi sostituirlo con 10, diciamo, y5per risparmiare spazio?
Sp3000,

@ Sp3000 risparmierebbe spazi bianchi ma nessun carattere complessivo. Ma penso di poter salvare un carattere comprimendo l'elenco memorizzandoK=5
Maltysen il

Nota che questa risposta non segue le regole in quanto "La tua soluzione deve risolvere qualsiasi caso di test di esempio in un minuto". (La citazione è in grassetto nella sezione Dettagli.)
randomra

0

C ++ - 863 byte, non modificato

Funziona abbastanza velocemente, nello stesso campo di gioco della soluzione scritta in Rust (circa 6 volte più veloce quando si compila con l'ottimizzazione attivata). Giocherò a golf più tardi questa sera (sera in Svezia, cioè).

#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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.