結果
問題 |
No.5021 Addition Pyramid
|
ユーザー |
|
提出日時 | 2025-02-25 22:24:34 |
言語 | Rust (1.83.0 + proconio) |
結果 |
AC
|
実行時間 | 1,862 ms / 2,000 ms |
コード長 | 5,842 bytes |
コンパイル時間 | 12,816 ms |
コンパイル使用メモリ | 403,036 KB |
実行使用メモリ | 32,596 KB |
スコア | 269,586,771 |
最終ジャッジ日時 | 2025-02-25 22:26:24 |
合計ジャッジ時間 | 107,230 ms |
ジャッジサーバーID (参考情報) |
judge7 / judge2 |
純コード判定しない問題か言語 |
(要ログイン)
ファイルパターン | 結果 |
---|---|
other | AC * 50 |
ソースコード
use std::cmp::{min, max}; use std::io::{self, BufRead}; const MOD: i32 = 100_000_000; fn error(x: u32, y: u32) -> u32 { let diff = if x > y { x - y } else { y - x }; min(diff, (MOD as u32).saturating_sub(diff)) } /// 上段 prev_row から,新たな行 new_row(長さ = prev_row.len() + 1)を, /// pivot_index 番目の値 pivot_val を自由変数として左右に順次決定して返す. fn compute_row(prev_row: &Vec<u32>, pivot_index: usize, pivot_val: u32) -> Vec<u32> { let n = prev_row.len(); let l = n + 1; let mut new_row = vec![0u32; l]; new_row[pivot_index] = pivot_val; // 右側を決定: new_row[j+1] = (prev_row[j] - new_row[j]) mod MOD for j in pivot_index..(l - 1) { let diff = (prev_row[j] as i32 - new_row[j] as i32).rem_euclid(MOD); new_row[j + 1] = diff as u32; } // 左側を決定: new_row[j-1] = (prev_row[j-1] - new_row[j]) mod MOD for j in (1..=pivot_index).rev() { let diff = (prev_row[j - 1] as i32 - new_row[j] as i32).rem_euclid(MOD); new_row[j - 1] = diff as u32; } new_row } /// rli: [start, end] の範囲を,count 個の値に等間隔(小数点は四捨五入)で分割した値を返す. fn rli(count: usize, start: i32, end: i32) -> Vec<i32> { let mut result = Vec::with_capacity(count); if count == 1 { result.push((start + end) / 2); } else { let step = (end - start) as f64 / (count - 1) as f64; for i in 0..count { let value = start as f64 + step * i as f64; result.push(value.round() as i32); } } result } /// beam_search /// - a: 目標ピラミッド.a[i] は第 i+1 段(0-indexed)の目標値ベクトル(長さ = i+1) /// - beam_width: 各段で保持する候補数 /// - delta_range: 自由変数(中央のセル)の候補として,ターゲット値からのずれ [-delta_range, delta_range] を試す /// - cand_num: 各段で自由変数に対して試す候補数(等間隔にサンプリング) /// /// 各候補は (current_row, current_max_err) のタプルで保持され, /// current_max_err はこれまでの段での最大誤差. /// 最終的に (底辺, max_error) のタプルを返す. fn beam_search(a: &Vec<Vec<u32>>, beam_width: usize, delta_range: i32, cand_num: usize) -> (Vec<u32>, u32) { let n = a.len(); // 初期ビーム: 第1段は [a[0][0]] だが, // 自由変数として a[0][0] - delta (delta ∈ RLI(beam_width, -delta_range, delta_range)) を候補とする let mut beam: Vec<(Vec<u32>, u32)> = Vec::new(); for delta in rli(beam_width, -delta_range, delta_range) { let candidate_val = ((a[0][0] as i32 - delta).rem_euclid(MOD)) as u32; beam.push((vec![candidate_val], delta.abs() as u32)); } // 各段を順次拡張していく(i = 1..n-1) for i in 1..n { let mut new_beam: Vec<(Vec<u32>, u32)> = Vec::new(); let l = i + 1; // 新たな行の自由変数として中央 (1-indexed で (l+1)//2 番目) を使う let pivot_index = (l + 1) / 2 - 1; for (row, curr_max_err) in &beam { let target_pivot = a[i][pivot_index]; // ターゲット値の前後 delta_range 内の候補(cand_num 個)を試す for delta in rli(cand_num, -delta_range, delta_range) { let pivot_val = ((target_pivot as i32 + delta).rem_euclid(MOD)) as u32; let new_row = compute_row(row, pivot_index, pivot_val); // 新たな行における各セルの誤差を計算 let mut row_max_err = 0u32; for j in 0..l { row_max_err = max(row_max_err, error(new_row[j], a[i][j])); } let new_max_err = max(*curr_max_err, row_max_err); new_beam.push((new_row, new_max_err)); } } // 評価(最大誤差)の小さい順にソートして、beam_width 個だけ残す new_beam.sort_by_key(|candidate| candidate.1); if new_beam.len() > beam_width { new_beam.truncate(beam_width); } beam = new_beam; } // 最終段で最も誤差の低い候補の底辺とその最大誤差を返す beam.into_iter().min_by_key(|candidate| candidate.1).unwrap() } fn main() { let stdin = io::stdin(); let mut lines = stdin.lock().lines().map(|l| l.unwrap()); // 最初の行は N let n: usize = lines.next().unwrap().trim().parse().expect("failed to parse N"); let mut a: Vec<Vec<u32>> = Vec::with_capacity(n); // 各段 i (1-indexed) は i 個の整数 for i in 1..=n { let mut row: Vec<u32> = Vec::with_capacity(i); while row.len() < i { if let Some(line) = lines.next() { for token in line.split_whitespace() { if row.len() < i { let num: u32 = token.parse().expect("failed to parse number"); row.push(num); } } } else { break; } } a.push(row); } // パラメータは必要に応じて調整してください let beam_width = 290; let delta_range = 14000000; let cand_num = beam_width; let (bottom_row, best_err) = beam_search(&a, beam_width, delta_range, cand_num); // スコアは 5×10⁷ - best_err とする let score = 50_000_000 - best_err as i32; eprintln!("Score: {}", score*50); // 底辺の各値を 1 行ずつ出力 let output_line = bottom_row.iter() .map(|num| num.to_string()) .collect::<Vec<_>>() .join(" "); println!("{}", output_line); }