Mencapai angka keberuntungan seseorang dalam reputasi

21

Pegolf kode baru, Joe, baru saja mendaftar ke situs. Dia memiliki 1 reputasi tetapi bertekad untuk mencapai semua angka keberuntungannya dalam reputasi dengan tepat. Joe percaya pada kekuatan yang lebih tinggi yang akan membantunya mencapai tujuannya dengan jumlah tindakan (atau orang lain) yang minimal. Sebagai pengguna baru, ia juga percaya bahwa reputasi negatif mungkin terjadi.

Anda harus menulis sebuah program atau fungsi yang membantu Joe menghitung berapa banyak tindakan yang harus dia harapkan.

Detail

  • Tindakan dapat mengubah reputasi dengan jumlah berikut (semua tindakan tersedia di setiap langkah terlepas dari aturan stackexchange):

    answer accepted:     +15
    answer voted up:     +10
    question voted up:    +5
    accepts answer:       +2
    votes down an answer: −1
    question voted down:  −2
    
  • Perubahan reputasi khusus lainnya diabaikan.

  • Angka keberuntungan bisa negatif dan dapat dicapai dengan urutan apa pun.
  • Solusi Anda harus menyelesaikan contoh uji kasus di bawah satu menit di komputer saya (saya hanya akan menguji kasus tutup. Saya memiliki PC di bawah rata-rata.).

Memasukkan

  • Angka keberuntungan Joe sebagai daftar bilangan bulat dalam bentuk umum bahasa Anda.

Keluaran

  • Jumlah tindakan minimum yang diperlukan sebagai bilangan bulat tunggal.
  • Output dapat dicetak ke stdout atau dikembalikan sebagai integer.

Contohnya

Input => Output (Contoh status reputasi)

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

Ini adalah kode-golf sehingga entri terpendek menang.

randomra
sumber

Jawaban:

1

C # - 501 Bytes

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

Kode tidak dikunci

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
sumber
16

Rust, 929 923 karakter

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

Ini sangat menyenangkan!


Komentar tentang implementasi

Jadi saya jelas tidak terlalu senang dengan ukurannya. Tetapi Rust juga sangat buruk dalam bermain golf. Namun, kinerjanya luar biasa.

Kode memecahkan setiap kasus uji dengan benar dalam waktu yang hampir seketika, sehingga kinerja jelas bukan masalah. Untuk bersenang-senang, inilah ujian yang jauh lebih sulit:

1234567 123456 12345 1234 123 777777 77777 7777 777

jawabannya adalah 82317, yang dapat diselesaikan oleh program saya pada laptop (kinerja sedang) saya dalam 1,66 detik (!), bahkan dengan algoritma jalur Hamiltonian brute-force rekursif.

Pengamatan

  • Pertama, kita harus membuat grafik berbobot yang dimodifikasi, dengan simpulnya masing-masing angka "beruntung" dan bobotnya adalah berapa banyak perubahan yang diperlukan untuk berpindah dari satu tingkat reputasi ke tingkat lainnya. Setiap pasangan node harus dihubungkan oleh dua sisi, karena naik tidak sama dengan turun dalam nilai reputasi (Anda bisa mendapatkan +10, misalnya, tetapi tidak -10).

  • Sekarang kita perlu mencari cara untuk menemukan jumlah minimum perubahan dari satu nilai rep ke yang lain.

    • Untuk mendapatkan dari nilai yang lebih tinggi ke nilai yang lebih rendah, itu sederhana: ambil saja di ceil((a - b) / 2)mana anilai lebih tinggi danb yang lebih rendah. Satu-satunya pilihan logis kami adalah menggunakan -2 sebanyak mungkin, dan -1 jika perlu.

    • Nilai rendah ke tinggi sedikit lebih rumit, karena menggunakan nilai terbesar yang mungkin tidak selalu optimal (mis. Untuk 0 hingga 9, solusi optimal adalah +10 -1). Namun, ini adalah masalah pemrograman buku dinamis, dan DP sederhana sudah cukup untuk menyelesaikannya.

  • Setelah kami menghitung perubahan minimum dari setiap angka ke setiap angka lainnya, kami pada dasarnya dibiarkan dengan sedikit varian TSP (masalah salesman keliling). Untungnya, ada jumlah node yang cukup kecil (maksimum 5 pada test case yang paling sulit) sehingga brute force cukup untuk langkah ini.

Kode tidak dikunci (banyak komentar)

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] }
}
Gagang pintu
sumber
1
Jawaban yang luar biasa! Saya tertarik pada Rust dan penjelasannya sebenarnya sangat membantu untuk belajar. Dan seperti kepala, Anda bisa mendapatkan sintaks dengan <!-- language-all: lang-rust -->. ;)
Alex A.
Saya sedang mengerjakan solusi, dan melihat bahwa jumlah minimal perubahan pada bobot rendah ke tinggi dapat dengan mudah dihitung dalam O (1) dengan menggunakan tabel pencarian yang sangat kecil, seperti dalam pseudo-code C seperti ini floor((a-b)/15)+{0,2,1,2,2,1,3,2,2,2,1,3,2,2,2}[(a-b)%15]. Solusi Anda mungkin dapat mengambil manfaat dari ini.
Fors
2

Pyth - 43 42 byte

Menggunakan pendekatan brute force sepenuhnya dengan semua permutasi dan kombinasi. Tidak ingin bermain golf lagi karena akan menerjemahkan ke Pyth. Diterjemahkan

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

Ini bahkan lebih lambat daripada versi python karena saya menggunakan filter daripada loop sementara. Penjelasan segera hadir, sekarang lihat kode Python.

Coba di sini 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

Bekerja pada yang kecil, tidak membiarkannya selesai pada yang besar.

Maltysen
sumber
Belum membaca kode dengan benar tetapi dapatkah Anda mengganti 10 dengan, katakanlah, y5untuk menghemat ruang kosong?
Sp3000
@ Sp3000 itu akan menghemat spasi tetapi tidak ada karakter secara keseluruhan. Tapi saya pikir saya bisa menyimpan char dengan mengompres daftar dengan menyimpanK=5
Maltysen
Perhatikan bahwa jawaban ini tidak mengikuti aturan karena "Solusi Anda harus menyelesaikan kasus uji contoh apa pun di bawah satu menit". (Kutipan dicetak tebal di bagian Detail.)
randomra
0

C ++ - 863 bytes, tidak diserang

Ini berjalan cukup cepat, di stadion baseball yang sama dengan solusi yang ditulis dalam Rust (sekitar 6 kali lebih cepat ketika kompilasi dengan optimasi dihidupkan). Saya akan bermain golf nanti malam ini (malam di Swedia).

#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;
}
Untuk S
sumber