녹, 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]}}
이것은 재미 있었다!
구현에 대한 논평
그래서 나는 크기에 너무 만족하지 않습니다. 그러나 녹은 어쨌든 골프에 절대적으로 끔찍합니다. 그러나 성능은 훌륭합니다.
이 코드는 거의 즉각적으로 각 테스트 사례를 올바르게 해결하므로 성능에는 문제가되지 않습니다. 재미를 위해 여기 훨씬 더 어려운 테스트 사례가 있습니다.
1234567 123456 12345 1234 123 777777 77777 7777 777
대답은입니다 82317
.이 프로그램은 재귀 적 무차별 해밀턴 경로 알고리즘을 사용하더라도 1.66 초 (!) 만 에 (중간 성능) 랩톱에서 해결할 수있었습니다 .
관찰
먼저 노드가 각각 "운이 좋은"숫자이고 가중치가 한 평판 수준에서 다른 평판 수준으로 변경되는 데 걸리는 변경 횟수를 기준으로 수정 된 가중치 그래프를 작성해야합니다. 위로 올라가는 것은 평판 값으로 내려가는 것과 같지 않기 때문에 각 노드 쌍은 두 개의 가장자리 로 연결되어야합니다 (예 : +10은 가능하지만 -10은 안 됨).
이제 한 rep 값에서 다른 rep 값으로 최소 변경 량을 찾는 방법을 찾아야합니다.
더 높은 값에서 더 낮은 값으로 이동하려면 간단합니다. 더 높은 값과 더 낮은 값 을 ceil((a - b) / 2)
어디에서 가져 가십시오 . 우리의 유일한 논리적 옵션은 가능한 한 -2를 사용하고 필요한 경우 -1을 사용하는 것입니다.a
b
가능한 가장 큰 값을 사용하는 것이 항상 최적이 아니기 때문에 (낮은 값에서 높은 값) 조금 더 복잡합니다 (예 : 0에서 9의 경우 최적 솔루션은 + 10-1). 그러나 이것은 교과서 동적 프로그래밍 문제이며 간단한 DP로 해결하기에 충분합니다.
각 숫자에서 다른 숫자로의 최소 변화를 계산 한 후에는 기본적으로 약간의 변형 된 TSP (여행사 문제)가 남아 있습니다. 운 좋게도,이 단계에는 무차별 대 입력으로 충분할 정도로 적은 수의 노드 (가장 어려운 테스트 사례에서는 최대 5 개)가 있습니다.
ungolfed 코드 (많이 주석 처리됨)
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 -->
. ;)