赢得声誉的幸运数字


21

一个新的代码窃贼,乔,刚刚注册到该站点。他享有1个声誉,但决心在声誉方面达到所有幸运数字。乔相信更高的权力,这将有助于他以最少的(他或他人)行动来实现自己的目标。作为新用户,他还认为负面声誉是可能的。

您应该编写一个程序或函数来帮助Joe计算他期望执行的动作数。

细节

  • 这些操作可以按以下数量更改信誉(与堆栈交换规则无关,所有操作在每一步都可用):

    answer accepted:     +15
    answer voted up:     +10
    question voted up:    +5
    accepts answer:       +2
    votes down an answer: −1
    question voted down:  −2
    
  • 其他特殊声誉更改将被忽略。

  • 幸运数字可以是负数,并且可以按任何顺序到达。
  • 您的解决方案必须在一分钟之内在我的计算机上解决任何示例测试用例(我将仅测试关闭用例。我的PC低于平均水平。)

输入项

  • Joe的幸运数字是您语言的一种常见形式的整数列表。

输出量

  • 需要作为单个整数的最小操作数。
  • 输出可以打印到标准输出或作为整数返回。

例子

输入=>输出(信誉状态示例)

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

这是代码高尔夫球,因此最短的条目将获胜。

Answers:


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

Rust,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秒(!)内在(中等性能)笔记本电脑上解决该问题。

观察结果

  • 首先,我们应该构建一个修改后的加权图,节点是每个“幸运”数字,权重是从一个信誉级别到另一个信誉级别需要进行多少更改。每对节点必须由两个边连接,因为信誉值的上升与下降并不相同(例如,可以得到+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 -->。;)
Alex A.

我正在研究一个解决方案,并且发现可以通过使用非常小的查找表在O(1)中轻松计算出从轻到重的最小变化量,就像这个类似C的伪代码一样floor((a-b)/15)+{0,2,1,2,2,1,3,2,2,2,1,3,2,2,2}[(a-b)%15]。您的解决方案可能会从中受益。
Fors 2015年

2

Pyth- 43 42字节

对所有排列和组合使用完全蛮力的方法。不希望更多地打高尔夫球,因为它将转化为Pyth。已翻译。

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

这比python版本还要慢,因为我使用filter而不是while循环。解释即将推出,现在看一下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

在小型计算机上工作,但没有在大型计算机上运行。


尚未正确阅读代码,但是您可以将其替换为10 y5以节省空白吗?
Sp3000

@ Sp3000它将节省空格,但不会节省任何字符。但我认为我可以通过存储压缩列表来保存字符K=5
-Maltysen

请注意,此答案不符合规则,因为“您的解决方案必须在一分钟内解决任何示例测试用例”。(“详细信息”部分中的行是粗体。)
randomra

0

C ++-863字节,未对齐

它运行速度非常快,与Rust编写的解决方案在同一范围内运行(在启用优化的情况下进行编译时,运行速度大约是其六倍)。我今晚晚些时候去打高尔夫球(那是在瑞典的晚上)。

#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;
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.