Rustで動的計画法の実装:Stones
はじめに
動的計画法を実装してみて、Rustの勉強をやってみる。問題としてはEducational DP Contestという動的計画法の練習を目的としたコンテストのものを使用。
AからZまで問題が設定されているが、今回はKのStones。石を交互に取っていって、取れなくなった方が負けというゲームの解放である。
利用したライブラリ
標準入力からデータを読み取る部分はproconio
を利用。
完成したコード
実装に難しい所はない。
use proconio::input;
fn main(){
input! {
n: usize, k:usize,
capture: [usize; n]
}
let k = k + 1;
let mut dp = vec![false; k];
for i in 0..k {
if dp[i] == false {
for j in 0..n {
if i + capture[j] < k { dp[i + capture[j]] = true; }
}
}
}
if dp[k - 1] == true {
println!("First");
} else {
println!("Second");
}
}
アルゴリズム
石の残りが0であれば「負け」。「負け」の状態になるように石が取れるなら「勝ち」。「勝ち」の状態になるようにしか石が取れないなら「負け」。ということで、石の数が0の状態から、「負け(false)」の状態に移動できる石の数を「勝ち(true)」として、石の数が
先行と後攻を意識して考えたくなるが、意識しなくて解けるところが面白いところだろうか?状態(石の数)で勝ち負けが決まってしまうというシンプルなゲームだからであるが。
ビット演算で解いてみる
出題されている問題において、石の数は最大bool
型の配列しか使っていないので、それを0
, 1
のビットで表現する。ビットが0
(負け)である時に、その状態に遷移可能な位置を示すcapture_mask
をシフトさせて論理和or
を取れば同じことが実現できる。
fn main(){
input! {
n: usize, k:usize,
capture: [i32; n]
}
let mut dp: u128 = 0;
let mut capture_mask: u128 = 0;
for i in 0..n {
capture_mask = capture_mask | (1u128 << (capture[i] - 1));
}
dp = dp | capture_mask;
for i in 0..k {
if dp & (1u128 << i) == 0u128 {
dp = dp | (capture_mask << (i + 1))
}
}
if dp & (1u128 << (k - 1)) == 0u128 {
println!("Second");
} else {
println!("First");
}
}
Rustにu128
型があったので、128ビット列を使っている。0ビット目を石の数が1個の場合としているので、
計算量の測定
関連記事
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