İtibarında birinin şanslı numaralarına ulaşmak


21

Yeni bir kod-golfçü Joe, siteye yeni girdi. 1 itibarı vardır ancak tüm şanslı numaralarına tam olarak itibarla ulaşmaya karar vermiştir. Joe, asgari miktarda (onun veya başkalarının) eylemleriyle hedefine ulaşmasına yardımcı olacak daha yüksek güçlere inanır. Yeni bir kullanıcı olarak, olumsuz itibarın mümkün olduğuna inanıyor.

Joe'nun kaç eylem beklemesi gerektiğini hesaplamasına yardımcı olacak bir program veya işlev yazmalısınız.

ayrıntılar

  • Eylemler, sayıları aşağıdaki miktarlara göre değiştirebilir (tüm işlemler stackexchange kurallarından bağımsız olarak her adımda kullanılabilir):

    answer accepted:     +15
    answer voted up:     +10
    question voted up:    +5
    accepts answer:       +2
    votes down an answer: −1
    question voted down:  −2
    
  • Diğer özel itibar değişiklikleri dikkate alınmaz.

  • Şanslı numaralar negatif olabilir ve herhangi bir sırayla ulaşılabilir.
  • Çözümünüz bilgisayarımdaki bir dakikadan az bir sürede herhangi bir test senaryosunu çözmelidir (sadece yakın vakaları test edeceğim. Ortalamanın altında bir PC'm var.).

Giriş

  • Joe, dilinizin ortak bir formunda bir tamsayı listesi olarak şanslı numaralar.

Çıktı

  • Tek bir tamsayı olarak gereken minimum işlem sayısı.
  • Çıktı stdout'a yazdırılabilir veya bir tamsayı olarak döndürülebilir.

Örnekler

Giriş => Çıkış (Örnek itibar durumları)

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

Bu kod golf yani en kısa giriş kazanır.

Yanıtlar:


1

501 bayt

551 Güncellemesi -> 501 Bayt

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 kod

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

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

Bu komikti!


Uygulama ile ilgili yorum

Bu yüzden belli ki büyüklüğü ile çok mutlu değilim. Ama Rust zaten golf oynamakta kesinlikle korkunç. Performans, ancak harika.

Kod, test durumlarının her birini anlık olarak yakın bir sürede doğru şekilde çözer, bu nedenle performans açıkça bir sorun değildir. Eğlence için, işte çok daha zor bir test durumu:

1234567 123456 12345 1234 123 777777 77777 7777 777

Cevabın olduğu 82317 , hangi programın özyinelemeli kaba kuvvet Hamiltoniyen yol algoritmasıyla bile (orta performanslı) dizüstü bilgisayarımda 1.66 saniyede (!) çözebildi .

Gözlemler

  • Öncelikle, düğümlerin her biri "şanslı" bir sayı ve ağırlıkları bir itibar seviyesinden diğerine almak için ne kadar değişiklik yapılması gerektiği ile değiştirilmiş bir ağırlıklı grafik oluşturmalıyız. Her bir düğüm çifti iki ile birbirine bağlanmalıdır. kenara , çünkü yukarı çıkmak itibar değerinde aşağı inmekle aynı değildir (örneğin, +10, ancak -10 alabilirsiniz).

  • Şimdi bir rep değerinden diğerine minimum değişiklik miktarını nasıl bulacağımızı bulmamız gerekiyor.

    • Daha yüksek bir değerden daha düşük bir değere ulaşmak için, basit: sadece en yüksek değerin ceil((a - b) / 2)olduğu yere gidin aveb alt değerdir. Tek mantıklı seçeneğimiz, mümkün olduğu kadar -2 ve sonra gerekirse -1'i kullanmak.

    • Düşükten yükseğe bir değer biraz daha karmaşıktır, çünkü mümkün olan en yüksek değeri kullanmak her zaman en uygun değildir (örneğin, 0 - 9 için en uygun çözüm +10 -1'dir). Ancak, bu bir ders kitabı dinamik programlama problemidir ve basit DP bunu çözmek için yeterlidir.

  • Her sayıdan diğer her sayıya olan asgari değişiklikleri hesapladığımızda, temelde hafif bir TSP değişkeni (seyahat eden satıcı problemi) kalır. Neyse ki, bu adım için kaba kuvvet yeterli olan az sayıda düğüm (en zor test durumunda maksimum 5) vardır.

Ungolfed kod (ağır yorum yaptı)

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
Müthiş cevap! Rust ile ilgileniyorum ve açıklama aslında öğrenme için çok yararlı. Ve sadece bir baş kadar gibi, sözdizimi ile vurgulayarak alabilirsiniz <!-- language-all: lang-rust -->. ;)
Alex A.

Bir çözüm üzerinde çalışıyorum ve düşük-yüksek ağırlıktaki minimum değişiklik miktarının bu C-benzeri sahte kodda olduğu gibi çok küçük bir arama tablosu kullanılarak O (1) 'de kolayca hesaplanabileceğini gördüm floor((a-b)/15)+{0,2,1,2,2,1,3,2,2,2,1,3,2,2,2}[(a-b)%15]. Çözümünüz muhtemelen bundan faydalanabilir.
Fors

2

Pyth - 43 42 bayt

Tüm permütasyon ve kombinasyonlarla tamamen kaba kuvvet yaklaşımı kullanır. Daha fazla golf oynamak istemiyorum çünkü Pyth'a tercüme edilecek. Çeviri.

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

Python sürümünden bile daha yavaştır, çünkü bir süre döngüsü yerine filtre kullanıyorum. Açıklama çok yakında geliyor, şimdi Python koduna bakın.

Burada çevrimiçi deneyin .

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

Küçükler üzerinde çalışmalar, büyükler üzerinde tamamlanmalarına izin vermedi.


Kodu düzgün okumamıştım, ancak y5beyaz boşluktan tasarruf etmek için 10 ile değiştirebilir misiniz ?
Sp3000,

@ Sp3000, boşluktan tasarruf sağlar ancak genel olarak herhangi bir karakter çıkarmaz. Ama sanırım listeyi sıkıştırarak bir karakterden tasarruf edebilirimK=5
Maltysen

Bu cevabın "Çözümünüzün herhangi bir örnek test vakasını bir dakika içinde çözmek zorunda olduğu" kurallarına uymadığını unutmayın. (Alıntı Ayrıntılar bölümünde koyu olarak belirtilmiştir.)
randomra

0

C ++ - 863 bayt, asılsız

Bu, Rust'ta yazılmış çözümle aynı oyun parkında oldukça hızlı çalışır (optimizasyon açıkken derleme yaparken yaklaşık 6 kat daha hızlı). Bu akşam daha sonra golf oynayacağım (İsveç'te akşam, yani).

#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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.