Atteindre ses numéros chanceux dans la réputation

21

Un nouveau golfeur de code, Joe, vient de s'inscrire sur le site. Il a 1 réputation mais déterminé à atteindre exactement tous ses numéros chanceux en réputation. Joe croit en des pouvoirs supérieurs qui l'aideront à atteindre son objectif avec un minimum d'actions (lui ou d'autres). En tant que nouvel utilisateur, il pense également qu'une réputation négative est possible.

Vous devez écrire un programme ou une fonction qui aide Joe à calculer le nombre d'actions à attendre.

Détails

  • Les actions peuvent modifier la réputation des montants suivants (toutes les actions sont disponibles à chaque étape, quelles que soient les règles d'échange de pile):

    answer accepted:     +15
    answer voted up:     +10
    question voted up:    +5
    accepts answer:       +2
    votes down an answer: −1
    question voted down:  −2
    
  • Les autres changements de réputation spéciaux ne sont pas pris en compte.

  • Les nombres chanceux peuvent être négatifs et peuvent être atteints dans n'importe quel ordre.
  • Votre solution doit résoudre tout exemple de cas de test en moins d'une minute sur mon ordinateur (je ne testerai que les cas fermés. J'ai un PC inférieur à la moyenne.).

Contribution

  • Les numéros porte-bonheur de Joe sous la forme d'une liste d'entiers dans une forme courante de votre langue.

Production

  • Nombre d'actions minimales nécessaires sous la forme d'un seul entier.
  • La sortie peut être imprimée sur stdout ou renvoyée sous forme d'entier.

Exemples

Input => Output (Exemples d'états de réputation)

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

Il s'agit de code-golf, donc l'entrée la plus courte l'emporte.

randomra
la source

Réponses:

1

C # - 501 octets

Mise à jour 551 -> 501 octets

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

Code non golfé

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
la source
16

Rouille, 929 923 caractères

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

C'était amusant!


Commentaire sur la mise en œuvre

Donc je ne suis évidemment pas trop satisfait de la taille Mais Rust est absolument terrible au golf de toute façon. La performance, cependant, est magnifique.

Le code résout correctement chacun des cas de test en un temps quasi instantané, donc les performances ne sont évidemment pas un problème. Pour le plaisir, voici un cas de test beaucoup plus difficile:

1234567 123456 12345 1234 123 777777 77777 7777 777

pour laquelle la réponse est 82317, que mon programme a pu résoudre sur mon ordinateur portable (de performance moyenne) en 1,66 seconde (!), même avec l'algorithme récursif de chemin hamiltonien à force brute.

Observations

  • Nous devons d'abord construire un graphique pondéré modifié, les nœuds étant chaque numéro "chanceux" et les poids étant le nombre de changements nécessaires pour passer d'un niveau de réputation à un autre. Chaque paire de nœuds doit être connectée par deux fronts, car monter n'est pas la même chose que descendre en valeur de réputation (vous pouvez obtenir +10, par exemple, mais pas -10).

  • Maintenant, nous devons trouver comment trouver le minimum de changements d'une valeur de répétition à l'autre.

    • Pour passer d'une valeur supérieure à une valeur inférieure, c'est simple: il suffit de prendre ceil((a - b) / 2)aest la valeur supérieure et bla valeur inférieure. Notre seule option logique est d'utiliser le -2 autant que possible, puis le -1 une fois si nécessaire.

    • Une valeur faible à élevée est un peu plus compliquée, car l'utilisation de la plus grande valeur possible n'est pas toujours optimale (ex. Pour 0 à 9, la solution optimale est +10 -1). Cependant, il s'agit d'un problème de programmation dynamique de manuels, et un simple DP suffit pour le résoudre.

  • Une fois que nous avons calculé les changements minimums de chaque numéro à chaque autre numéro, nous nous retrouvons essentiellement avec une légère variante de TSP (problème du voyageur de commerce). Heureusement, il y a un nombre suffisamment petit de nœuds (un maximum de 5 dans le cas de test le plus difficile) que la force brute est suffisante pour cette étape.

Code non golfé (fortement commenté)

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] }
}
Poignée de porte
la source
1
Réponse géniale! Je m'intéresse à Rust et l'explication est en fait très utile pour l'apprentissage. Et juste comme un avertissement, vous pouvez obtenir la coloration syntaxique avec <!-- language-all: lang-rust -->. ;)
Alex A.
Je travaille sur une solution, et vu que la quantité minimale de changements sur le faible poids élevé peut facilement être calculé en O (1) à l'aide d' une très petite table de recherche, comme dans ce type C pseudo-code floor((a-b)/15)+{0,2,1,2,2,1,3,2,2,2,1,3,2,2,2}[(a-b)%15]. Votre solution pourrait probablement en bénéficier.
Fors
2

Pyth - 43 42 octets

Utilise une approche complètement brutale avec toutes les permutations et combinaisons. Ne cherchez plus à jouer au golf car cela se traduira par Pyth. Traduit.

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

C'est encore plus lent que la version python car j'utilise un filtre au lieu d'une boucle while. Explication à venir, regardez maintenant le code Python.

Essayez-le ici en ligne .

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

Fonctionne sur les petits, ne l'a pas laissé se terminer sur les grands.

Maltysen
la source
Vous n'avez pas lu le code correctement, mais pouvez-vous remplacer 10 par, disons, y5pour économiser sur les espaces?
Sp3000
@ Sp3000, cela permettrait d'économiser des espaces, mais pas de caractères dans l'ensemble. Mais je pense que je peux enregistrer un caractère en compressant la liste en stockantK=5
Maltysen
Notez que cette réponse ne suit pas les règles car "Votre solution doit résoudre tout exemple de cas de test en moins d'une minute". (La citation est en gras dans la section Détails.)
randomra
0

C ++ - 863 octets, non golfé

Cela fonctionne assez rapidement, dans le même stade que la solution écrite en Rust (environ 6 fois plus rapide lors de la compilation avec l'optimisation activée). Je jouerai au golf plus tard ce soir (soirée en Suède, c'est-à-dire).

#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
la source