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] }
}
<!-- language-all: lang-rust -->. ;)