Đạt được con số may mắn trong danh tiếng


21

Một người chơi mã mới, Joe, vừa đăng ký vào trang web. Anh ta có 1 danh tiếng nhưng quyết tâm đạt được tất cả các con số may mắn của mình bằng danh tiếng. Joe tin vào những sức mạnh cao hơn sẽ giúp anh ta đạt được mục tiêu của mình với số lượng hành động tối thiểu (của anh ta hoặc của người khác). Là một người dùng mới, ông cũng tin rằng danh tiếng tiêu cực là có thể.

Bạn nên viết một chương trình hoặc chức năng giúp Joe tính toán số lượng hành động anh ta mong đợi.

Chi tiết

  • Các hành động có thể thay đổi danh tiếng theo số tiền sau (tất cả các hành động đều có sẵn ở mọi bước bất kể quy tắc stackexchange):

    answer accepted:     +15
    answer voted up:     +10
    question voted up:    +5
    accepts answer:       +2
    votes down an answer: −1
    question voted down:  −2
    
  • Thay đổi danh tiếng đặc biệt khác được coi thường.

  • Con số may mắn có thể là số âm và có thể đạt được theo bất kỳ thứ tự nào.
  • Giải pháp của bạn phải giải quyết bất kỳ trường hợp kiểm tra ví dụ nào dưới một phút trên máy tính của tôi (tôi sẽ chỉ kiểm tra các trường hợp gần. Tôi có một PC dưới mức trung bình.).

Đầu vào

  • Những con số may mắn của Joe như một danh sách các số nguyên dưới dạng ngôn ngữ chung của bạn.

Đầu ra

  • Số lượng hành động tối thiểu cần thiết như một số nguyên duy nhất.
  • Đầu ra có thể được in ra thiết bị xuất chuẩn hoặc trả về dưới dạng số nguyên.

Ví dụ

Đầu vào => Đầu ra (Ví dụ về trạng thái danh tiếng)

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

Đây là môn đánh gôn nên bài dự thi ngắn nhất sẽ thắng.

Câu trả lời:


1

C # - 501 byte

Cập nhật 551 -> 501 byte

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

Mã bị đánh cắp

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 ký tự

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

Điều này thật thú vị!


Bình luận về việc thực hiện

Vì vậy, rõ ràng tôi không quá hài lòng với kích thước. Nhưng dù sao thì Rust hoàn toàn khủng khiếp khi chơi golf. Hiệu suất, tuy nhiên, là tuyệt vời.

Mã giải quyết chính xác từng trường hợp thử nghiệm trong một khoảng thời gian gần như tức thời, do đó hiệu suất rõ ràng không phải là vấn đề. Để giải trí, đây là một trường hợp thử nghiệm khó khăn hơn nhiều:

1234567 123456 12345 1234 123 777777 77777 7777 777

câu trả lời là gì 82317, mà chương trình của tôi có thể giải quyết trên máy tính xách tay (hiệu suất trung bình) của tôi trong 1,66 giây (!), ngay cả với thuật toán đường dẫn Hamilton đệ quy.

Quan sát

  • Trước tiên, chúng ta nên xây dựng một biểu đồ có trọng số được sửa đổi, với các nút là mỗi số "may mắn" và trọng số là có bao nhiêu thay đổi để có được từ cấp độ danh tiếng này sang cấp độ danh tiếng khác. Mỗi cặp nút phải được kết nối bởi hai cạnh, vì đi lên không giống như đi xuống trong giá trị danh tiếng (ví dụ, bạn có thể nhận được +10, nhưng không phải -10).

  • Bây giờ chúng ta cần tìm ra cách tìm ra lượng thay đổi tối thiểu từ giá trị rep này sang giá trị rep khác.

    • Để nhận được từ một giá trị cao hơn một giá trị thấp hơn, nó đơn giản: chỉ cần lấy ceil((a - b) / 2)ở đâu alà giá trị cao hơn và blà giá trị thấp hơn. Tùy chọn logic duy nhất của chúng tôi là sử dụng -2 càng nhiều càng tốt, và sau đó là -1 một lần nếu cần thiết.

    • Giá trị thấp đến cao phức tạp hơn một chút, vì sử dụng giá trị lớn nhất có thể không phải lúc nào cũng tối ưu (ví dụ: từ 0 đến 9, giải pháp tối ưu là +10 -1). Tuy nhiên, đây là một vấn đề lập trình động trong sách giáo khoa, và DP đơn giản là đủ để giải quyết nó.

  • Khi chúng tôi đã tính toán các thay đổi tối thiểu từ mỗi số này sang mỗi số khác, về cơ bản chúng tôi chỉ còn lại một biến thể TSP (vấn đề nhân viên bán hàng du lịch). May mắn thay, có một số lượng nút đủ nhỏ (tối đa là 5 trong trường hợp kiểm tra khó nhất) mà lực lượng vũ phu là đủ cho bước này.

Mã Ungolfed (bình luận nặng nề)

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
Câu trả lời tuyệt vời! Tôi quan tâm đến Rust và lời giải thích thực sự rất hữu ích cho việc học. Và chỉ cần ngẩng cao đầu, bạn có thể làm nổi bật cú pháp <!-- language-all: lang-rust -->. ;)
Alex A.

Tôi đang nghiên cứu một giải pháp và thấy rằng số lượng thay đổi tối thiểu trên trọng lượng thấp đến cao có thể dễ dàng được tính bằng O (1) bằng cách sử dụng bảng tra cứu rất nhỏ, như trong mã giả giống như C này floor((a-b)/15)+{0,2,1,2,2,1,3,2,2,2,1,3,2,2,2}[(a-b)%15]. Giải pháp của bạn có thể có lợi từ việc này.
Dành cho

2

Pyth - 43 42 byte

Sử dụng phương pháp tiếp cận hoàn toàn vũ phu với tất cả các hoán vị và kết hợp. Không tìm đến golf nhiều hơn vì sẽ dịch sang Pyth. Đã dịch.

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

Điều này thậm chí còn chậm hơn phiên bản python vì tôi sử dụng bộ lọc thay vì vòng lặp while. Sắp có giải thích, bây giờ hãy xem mã Python.

Hãy thử nó ở đây trực tuyến .

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

Hoạt động trên những cái nhỏ, không cho phép nó chạy để hoàn thành trên những cái lớn.


Bạn không đọc mã đúng nhưng bạn có thể thay thế 10 bằng cách nói y5để tiết kiệm khoảng trắng không?
Sp3000

@ Sp3000 nó sẽ tiết kiệm khoảng trắng nhưng không phải bất kỳ ký tự tổng thể nào. Nhưng tôi nghĩ rằng tôi có thể lưu một char bằng cách nén danh sách bằng cách lưu trữK=5
Maltysen

Lưu ý rằng câu trả lời này không tuân theo các quy tắc như "Giải pháp của bạn phải giải quyết bất kỳ trường hợp thử nghiệm ví dụ nào dưới một phút". (Trích dẫn được in đậm trong phần Chi tiết.)
Randomra

0

C ++ - 863 byte, không được cung cấp

Điều này chạy khá nhanh, trong cùng một sân bóng như giải pháp được viết bằng Rust (nhanh gấp khoảng 6 lần khi biên dịch với tối ưu hóa được bật). Tôi sẽ chơi golf vào tối nay (tối nay ở Thụy Điển).

#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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.