Im Ruf die Glückszahlen erreichen

21

Ein neuer Code-Golfer, Joe, hat sich gerade auf der Seite registriert. Er hat 1 Ruf, ist aber entschlossen, alle seine Glückszahlen im Ruf genau zu erreichen. Joe glaubt an höhere Kräfte, die ihm helfen, sein Ziel mit einem Minimum an (seinen oder anderen) Handlungen zu erreichen. Als neuer Benutzer glaubt er auch, dass eine negative Reputation möglich ist.

Sie sollten ein Programm oder eine Funktion schreiben, mit deren Hilfe Joe berechnen kann, wie viele Aktionen er erwarten soll.

Einzelheiten

  • Die Aktionen können die Reputation um die folgenden Beträge ändern (alle Aktionen sind bei jedem Schritt verfügbar, unabhängig von den Stapelaustauschregeln):

    answer accepted:     +15
    answer voted up:     +10
    question voted up:    +5
    accepts answer:       +2
    votes down an answer: −1
    question voted down:  −2
    
  • Andere spezielle Reputationsänderungen bleiben unberücksichtigt.

  • Glückszahlen können negativ sein und in beliebiger Reihenfolge erreicht werden.
  • Ihre Lösung muss jeden Beispieltestfall auf meinem Computer in weniger als einer Minute lösen (ich teste nur enge Fälle. Ich habe einen unterdurchschnittlichen PC.).

Eingang

  • Joes Glückszahlen als Liste von ganzen Zahlen in einer gemeinsamen Form Ihrer Sprache.

Ausgabe

  • Die Anzahl der Mindestaktionen, die als einzelne Ganzzahl benötigt werden.
  • Die Ausgabe kann auf stdout gedruckt oder als Ganzzahl zurückgegeben werden.

Beispiele

Eingabe => Ausgabe (Beispiel Reputationsstatus)

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

Dies ist Code-Golf, also gewinnt der kürzeste Eintrag.

randomra
quelle

Antworten:

1

C # - 501 Bytes

Aktualisieren Sie 551 -> 501 Bytes

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

Ungolfed Code

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;
        }
    }
}
Ruben-J
quelle
16

Rust, 929 923 Zeichen

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

Das hat Spaß gemacht!


Kommentar zur Implementierung

Mit der Größe bin ich also offensichtlich nicht sehr zufrieden. Aber Rust ist sowieso absolut schrecklich im Golfen. Die Leistung ist jedoch wunderbar.

Der Code löst jeden Testfall innerhalb kürzester Zeit korrekt, sodass die Leistung offensichtlich kein Problem darstellt. Zum Spaß ist hier ein viel schwierigerer Testfall:

1234567 123456 12345 1234 123 777777 77777 7777 777

wofür lautet die Antwort 82317, die mein Programm auf meinem (mittelschweren) Laptop in 1,66 Sekunden (!) auch mit dem rekursiven Brute-Force-Hamilton-Pfad-Algorithmus lösen konnte .

Beobachtungen

  • Zuerst sollten wir ein modifiziertes gewichtetes Diagramm erstellen, wobei die Knoten die einzelnen "Glückszahlen" und die Gewichte die Anzahl der Änderungen sind, die erforderlich sind, um von einer Reputationsstufe zur anderen zu gelangen. Jedes Knotenpaar muss durch zwei Kanten verbunden sein, da ein Anstieg nicht mit einem Absinken des Reputationswerts identisch ist (Sie können beispielsweise +10, aber nicht -10 erhalten).

  • Nun müssen wir herausfinden, wie die minimale Anzahl von Änderungen von einem Wiederholungswert zu einem anderen ermittelt werden kann.

    • Um von einem höheren Wert zu einem niedrigeren Wert zu gelangen, ist es einfach: Nimm einfach, ceil((a - b) / 2)wo ader höhere Wert und bder niedrigere Wert ist. Unsere einzige logische Option besteht darin, das -2 so oft wie möglich zu verwenden und dann das -1, falls erforderlich, einmal.

    • Ein niedriger bis hoher Wert ist etwas komplizierter, da die Verwendung des größtmöglichen Werts nicht immer optimal ist (z. B. für 0 bis 9 ist die optimale Lösung +10 -1). Dies ist jedoch ein dynamisches Programmierproblem im Lehrbuch, und zur Lösung dieses Problems reicht ein einfaches DP aus.

  • Sobald wir die minimalen Änderungen von jeder Zahl zu jeder anderen Zahl berechnet haben, bleibt uns im Wesentlichen eine leichte Variante von TSP (Problem des Handlungsreisenden). Glücklicherweise gibt es eine kleine Anzahl von Knoten (maximal 5 im härtesten Testfall), die für diesen Schritt ausreichen.

Ungolfed Code (stark kommentiert)

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] }
}
Türknauf
quelle
1
Geniale Antwort! Ich interessiere mich für Rust und die Erklärung ist eigentlich sehr hilfreich zum Lernen. Und nur als Heads-Up können Sie Syntax-Hervorhebungen mit erhalten <!-- language-all: lang-rust -->. ;)
Alex A.
Ich arbeite an einer Lösung und habe festgestellt, dass die minimalen Änderungen des niedrigen zu hohen Gewichts leicht in O (1) berechnet werden können, indem eine sehr kleine Nachschlagetabelle verwendet wird, wie in diesem C-ähnlichen Pseudocode floor((a-b)/15)+{0,2,1,2,2,1,3,2,2,2,1,3,2,2,2}[(a-b)%15]. Ihre Lösung könnte wahrscheinlich davon profitieren.
Fors
2

Pyth - 43 42 Bytes

Verwendet bei allen Permutationen und Kombinationen den Brute-Force-Ansatz. Ich möchte nicht mehr Golf spielen, da dies zu Pyth führt. Übersetzt.

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

Dies ist sogar langsamer als die Python-Version, da ich Filter anstelle einer while-Schleife verwende. Erklärung in Kürze, schauen Sie sich jetzt den Python-Code an.

Probieren Sie es hier 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

Arbeiten an den Kleinen, ließen es bei den Großen nicht zu Ende laufen.

Maltysen
quelle
Sie haben den Code nicht richtig gelesen, aber können Sie 10 durch beispielsweise y5Whitespace ersetzen ?
Sp3000,
@ Sp3000 es würde whitespace sparen aber insgesamt keine zeichen. Aber ich denke, ich kann ein K=5
Zeichen
Beachten Sie, dass diese Antwort nicht den Regeln entspricht, da "Ihre Lösung einen Beispieltestfall in weniger als einer Minute lösen muss". (Das Zitat ist im Abschnitt "Details" fett gedruckt.)
randomra
0

C ++ - 863 Bytes, ungolfed

Dies läuft ziemlich schnell im selben Ballpark wie die in Rust geschriebene Lösung (ungefähr 6-mal so schnell beim Kompilieren mit aktivierter Optimierung). Ich werde heute Abend noch Golf spielen (also abends in Schweden).

#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;
}
Fors
quelle