評判の幸運な数に達する


21

サイトに登録したばかりの新しいコードゴルファー、ジョー。彼は1つの評判を持っていますが、評判の彼のすべての幸運な数に正確に到達することを決定しました。ジョーは、自分(または他の人)の最小限のアクションで目標を達成するのに役立つ、より高い力を信じています。新しいユーザーとして、彼は否定的な評判も可能だと考えています。

ジョーが期待するアクションの数を計算するのに役立つプログラムまたは関数を作成する必要があります。

詳細

  • アクションは、以下の量だけレピュテーションを変更できます(スタック交換ルールに関係なく、すべてのステップですべてのアクションが利用可能です)。

    answer accepted:     +15
    answer voted up:     +10
    question voted up:    +5
    accepts answer:       +2
    votes down an answer: −1
    question voted down:  −2
    
  • その他の特別な評判の変更は無視されます。

  • ラッキーナンバーは負の場合があり、任意の順序で到達できます。
  • あなたの解決策は、私のコンピューターで1分以内すべてのサンプルテストケースを解決する必要があります(近くのケースのみをテストします。平均以下のPCを持っています)。

入力

  • あなたの言語の一般的な形式の整数のリストとしてのジョーのラッキーナンバー。

出力

  • 単一の整数として必要な最小アクションの数。
  • 出力はstdoutに出力されるか、整数として返されます。

入力=>出力(評価状態の例)

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

これはコードゴルフなので、最短のエントリーが勝ちます。

回答:


1

C#-501バイト

551の更新 -> 501バイト

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

未ゴルフコード

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

錆、929 923文字

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

これは楽しかった!


実装に関する解説

だから、私は明らかにサイズにあまり満足していません。しかし、とにかくRustはゴルフでは絶対にひどいです。しかし、パフォーマンスは素晴らしいです。

コードは、ほぼ瞬時に各テストケースを正しく解決するため、パフォーマンスは明らかに問題ではありません。楽しみのために、ここにはるかに難しいテストケースがあります:

1234567 123456 12345 1234 123 777777 77777 7777 777

答えは 82317、私のプログラムが私の(中性能)ラップトップで解決できた再帰的なブルートフォースハミルトニアンパスアルゴリズムを使用しても 1.66秒(!)で。

観察

  • 最初に、ノードがそれぞれ「幸運な」数であり、重みがレピュテーションレベルから別のレピュテーションレベルに到達するのに必要な変更数である、修正された重み付きグラフを作成する必要があります。ノードの各ペアは2つで接続する必要がありますエッジで上昇するとレピュテーション値が低下するのと同じではないためです(たとえば、-10ではなく+10を取得できます)。

  • 次に、ある担当者の値から別の担当者の値への変更の最小量を見つける方法を見つける必要があります。

    • 高い値から低い値に変更するのは簡単です。高い値がceil((a - b) / 2)どこにaあるかを確認してくださいb低い値です。唯一の論理オプションは、可能な限り-2を使用し、必要に応じて-1を使用することです。

    • 可能な限り最大の値を使用することが常に最適であるとは限らないため、低い値から高い値は少し複雑です(たとえば、0〜9の場合、最適なソリューションは+10 -1です)。ただし、これは教科書の動的プログラミングの問題であり、単純なDPで問題を解決できます。

  • 各番号から他のすべての番号への最小変化を計算すると、TSP(旅行セールスマンの問題)のわずかなバリエーションが残ります。幸いなことに、このステップではブルートフォースで十分な数のノードがあります(最も難しいテストケースでは最大5つ)。

ゴルフされていないコード(コメントが多い)

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
素晴らしい答え!私はRustに興味があり、説明は実際に学習に非常に役立ちます。そして、頭に浮かぶように、構文の強調表示を取得できます<!-- language-all: lang-rust -->。;)
アレックスA.

私は解決策に取り組んでおり、このCのような擬似コードのように、非常に小さなルックアップテーブルを使用することで、O(1)で低から高の重みの最小限の変更を簡単に計算できることがわかりましたfloor((a-b)/15)+{0,2,1,2,2,1,3,2,2,2,1,3,2,2,2}[(a-b)%15]。あなたのソリューションはおそらくこれから恩恵を受けるでしょう。
-Fors

2

Pyth - 43 42バイト

すべての順列と組み合わせで完全にブルートフォースアプローチを使用します。Pythに翻訳されるため、ゴルフをもっと見ない。翻訳済み。

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

whileループの代わりにフィルターを使用するため、これはpythonバージョンよりもさらに遅くなります。説明はすぐに来るので、Pythonコードを見てみましょう。

こちらからオンラインでお試しください

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

小さなもので動作しますが、大きなものでは完了しませんでした。


コードを適切に読み取れませんでしたが、たとえばy5スペースを節約するために10を置き換えることができますか?
Sp3000

@ Sp3000は、空白を保存しますが、文字全体は保存しません。しかし、私は保存することにより、リストを圧縮して文字を救うことができると思うK=5
Maltysen

この解答は、「あなたのソリューションは、1分以内にテストケースの例を解決する必要がある」というルールに従っていないことに注意してください。(詳細セクションで引用

0

C ++-863バイト、未使用

これは、Rustで書かれたソリューションと同じ球場で、かなり高速に実行されます(最適化を有効にしてコンパイルする場合、約6倍高速です)。今晩遅くにゴルフをします(スウェーデンの夜、つまり)。

#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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.