Rustで動的計画法:Tower
はじめに
動的計画法を実装してみて、Rustの勉強をやってみる。問題としてはEducational DP Contestという動的計画法の練習を目的としたコンテストのものを使用。
AからZまで問題が設定されているが、今回はXのTowerを解いてみる。丈夫さ
利用したライブラリ
標準入力からデータを読み取る部分はproconio
を利用。
chmax
のマクロも利用。
解き方
総当たり
ブロックを「積んでいく」問題なのだけど、積む方向で考えると、新しいブロックを積むたびに既存のブロックの「丈夫さ」が、新しく積んだブロックの重さを足しても耐えれるかの確認を行う必要がある。既に「積んだ」ものを意識しないようにしたいので、「積む」のではなく「下に新しいブロックを追加する」方向で考える。こうすると「既に積んであるブロックの重さ」が「新しく追加するブロックの丈夫さ」以下かどうかのチェックで済む。
そこで総当たりで求めると以下のようになる。計算量は
use proconio::input;
fn stack_block(remaining_blocks:&Vec<(usize, usize, i64)>, stacked_weight:usize, stacked_value:i64) -> i64 {
let mut value = stacked_value;
for i in 0..remaining_blocks.len() {
let (w, s, v) = remaining_blocks[i];
if s >= stacked_weight {
let mut blocks = remaining_blocks.clone();
blocks.remove_swap(i);
chmax!(value, stack_block(&blocks, stacked_weight + w, stacked_value + v));
}
}
value
}
fn main() {
input! {
n : usize,
mut blocks : [(usize, usize, i64); n]
}
let answer = stack_block(&blocks, 0, 0);
println!("{}", answer);
}
動的計画法
動的計画法でこういったものを解くときはブロックを1つずつ追加していくループを回すのが基本である。また、新しいブロックを下に追加する時、考えないといけないのは既に積み上がっているブロックの重さの合計が、新しいブロックの丈夫さを越えないかどうかである。このため、
let mut dp = vec![0; s_max + w_max + 1];
for (w, s, v) in blocks {
let mut next_dp = dp.clone();
for i in 0..=s {
chmax!(next_dp[i + w], dp[i] + v);
}
dp = next_dp;
}
ただ、これだけでは答えはでてこない。処理としては
なので、
use proconio::input;
use std::cmp;
fn main() {
input! {
n : usize,
mut blocks : [(usize, usize, i64); n]
}
let (w_max, s_max) = blocks.iter().fold((usize::MIN, usize::MIN), |(wm, sm), (w, s, _)| (cmp::max(wm, *w), cmp::max(sm, *s)));
blocks.sort_by(|(wa, sa, _), (wb, sb, _)| (wa + sa).cmp(&(wb + sb)));
let mut dp = vec![0; s_max + w_max + 1];
for (w, s, v) in blocks {
let mut next_dp = dp.clone();
for i in 0..=s {
chmax!(next_dp[i + w], dp[i] + v);
}
dp = next_dp;
}
let answer = dp.iter().max().unwrap();
println!("{}", answer);
測定結果
ブロックの数と処理時間の関係を計測した結果が以下のグラフである。
総当たり(brute force)が
今回、2つのベクタを使って処理したが、for
文の順番さえ気をつければ1本のベクタで処理できる。この時、clone()
分の処理は軽くなる。ただ、処理自体は
for (w, s, v) in blocks {
-
+ for i in (0..=s).rev() {
chmax!(dp[i + w], dp[i] + v);
}
-
}
関連記事
Rustで動的計画法の実装:🐸Frog | 🌴Vacation | 🎒Knapsack | 🐍LCS | 🚶♂️Longest Path | 🕸️Grid | 💰Coins | 🍣Sushi | 🪨Stones | 📐dequeue | 🍬Candies | 🫥Slimes | 💑Matching | 🌲Indipendent Set | 🌻Flowers | 👣Walk | 🖥️Digit Sum | 🎰Permutation | 🐰Grouping | 🌿Subtree | ⏱️Intervals | 🗼Tower | 🐸Frog3
Discussion