Rustで動的計画法の実装 : Vacation
はじめに
動的計画法を実装してみて、Rustの勉強をやってみる。問題としてはEducational DP Contestという動的計画法の練習を目的としたコンテストのものを使用。
AからZまで問題が設定されているが、今回はCのVacation。1日3種類の行動の中から選択して休暇中の幸福度を最大化する問題。ただし、二日連続で同じ行動はできない。
利用したライブラリ
chmax
は検索するといくつかでてくるので、それを利用。
標準入力からデータを読み取る部分はproconio
を利用。
完成したコード
「3種類の行動」ということになっていたけど、そこも変更可能にしたいよね。ということでACTION_NUM
を変更することで行動が3つ以外の問題にも対応した。
use itertools::Itertools;
const ACTION_NUM : usize = 3;
fn main(){
input!(
n: usize,
happiness: [[i64; ACTION_NUM]; n],
);
let mut dp = vec![vec![0; ACTION_NUM]; n];
dp[0] = happiness[0].clone();
for i in 1..n {
for x in (0..ACTION_NUM).permutations(2) {
chmax!(dp[i][x[0]], dp[i-1][x[1]] + happiness[i][x[0]]);
}
}
let answer = dp[n-1].iter().max().unwrap();
println!("{}", answer);
}
基本的なアルゴリズム
dp[i][j]
とする[1]。happiness[i][j]
に入っているので、以下のように書ける。二日連続で同じ行動はできないので、前日の行動i != k
)必要がある。
for j in 0..ACTION_NUM {
for k in 0..ACTION_NUM {
if j != k {
chmax!(dp[i][j], dp[i-1][k] + happiness[i][j]);
}
}
}
ただ、2重ループになるし、条件文も入るのでちょっとソースの見通しが悪い。そこで最終的には以下のようにした。
for x in (0..ACTION_NUM).permutations(2) {
chmax!(dp[i][x[0]], dp[i-1][x[1]] + happiness[i][x[0]]);
}
permutations
はitertools::Itertools
に含まれており、順列を返す。つまり
for e in (0..3).permutations(2) {
print!("{:?}", e);
}
[0, 1][0, 2][1, 0][1, 2][2, 0][2, 1]
二日連続で同じ行動をすることを許容するなら直積(product
)を使えば良いが、そもそもその時は、毎日一番幸福度の高い行動をとる貪欲な方法で最適解が出る。
結果
結果はdp[n-1]
)で一番高い幸福度を求める。
let answer = dp[n-1].iter().max().unwrap();
max()
の返り値としては、エラーかどうかも返すためにOption<&T>
型になっている。正常時であればSome(&17)
のように帰ってくるし、空行列だったりエラーがあった場合は、None
が帰ってくる。Some(&17)
を&17
にするためにunwrap()
が必要になる。
初期値
1日目はどの行動を行なってもいいので、happiness[0]をそのままコピーする。
let mut dp = vec![vec![0; ACTION_NUM]; n];
dp[0] = happiness[0].clone();
最初は、vecのvecではなく、配列のvecにしていたのではまってしまった。
let mut dp = vec![[0; ACTION_NUM]; n];
dp[0] = happiness[0];
dp[0]
は配列だけど、happiness[0]
はvec型なので入力ができずにコンパイルできなかった。何か方法があるのかもしれないが、分からなかったので、vecのvecとした。
dp[0]は0で初期化して、dp[1]を1日目として動かしても動作するし、問題文には合っている気がする。しかし、happinessが0-originになっているので、それに合わせている。0-originと1-originを混合させるのはバグの元になると思う。
関連記事
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
-
正確には0-originなのでdp[i-1][j] ↩︎
Discussion