C - Yamanote Line Game Editorial by rsk0315


インタラクティブ問題の Rust での実装に関する話を書きます。

proconio (doc) を使っているユーザが多いと思われますが、これは --release とそうでないときで挙動が異なります。 具体的には、--release のときは「入力を一通り読んでから処理を行う」のに対し、そうでないときは一行ずつ読んで処理を行います。

そのため、「ローカルで(デバッグビルドで)実行したときはうまくいくのに」「コードテストで一通り入力を与えて実行したときはうまくいくのに」といった事象が起きたりします。(補足:ジャッジはリリースビルドで行われます)

そこで、普段は陽には指定しないことが多い source というのを指定することで、リリースビルドでも一行ずつ読んでもらうことができます。

use std::collections::BTreeSet;
use std::io::{stdin, stdout, BufReader, Write};
 
use proconio::{input, source::line::LineSource};
 
fn main() {
    let stdin = stdin();
    let mut source = LineSource::new(BufReader::new(stdin.lock()));
 
    input! {
        from &mut source,
        n: usize,
    }
 
    let mut rem: BTreeSet<_> = (1..=2 * n + 1).collect();
    for _ in 0..=n {
        let x = *rem.iter().next().unwrap();
        println!("{}", x);
        stdout().flush().unwrap();
        rem.remove(&x);
        input! {
            from &mut source,
            y: usize,
        }
        // remove(&0) しても問題ないので適当に
        rem.remove(&y);
    }
}

また、実装方針として、「x を出力したときのジャッジからの返答を返す関数 f(x) を定義しておく」というのがあります。この問題ではあまりメリットはないですが、「同じ出力方法を複数回行うときに一箇所だけ flush を忘れる」といったミスを防ぎやすくてよいです。

そうした際は、次のように書くとよいでしょう。

use std::collections::BTreeSet;
use std::io::{stdin, stdout, BufRead, BufReader, Write};

use proconio::{input, source::line::LineSource};

fn main() {
    let stdin = stdin();
    let mut source = LineSource::new(BufReader::new(stdin.lock()));

    input! {
        from &mut source,
        n: usize,
    }

    let mut rem: BTreeSet<_> = (1..=2 * n + 1).collect();
    for _ in 0..=n {
        let x = *rem.iter().next().unwrap();
        rem.remove(&x);
        let reply = query(x, &mut source);
        rem.remove(&reply);
    }
}

fn query<R: BufRead>(x: usize, source: &mut LineSource<R>) -> usize {
    println!("{}", x);
    stdout().flush().unwrap();
    input! {
        from source,
        y: usize,
    }
    y
}

querysource を渡さずに関数呼び出し毎に stdin.lock() を呼びたい場合は注意が必要です。 スコープを抜けないと lock が解放されないため、期待通りに動いてくれません。


追記: #[fastout] をつけていると「main() の終了時に全ての出力をまとめて書き出す」といった挙動になるので、インタラクティブ問題の際には外しましょう。

posted:
last update: