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
}
query
に source
を渡さずに関数呼び出し毎に stdin.lock()
を呼びたい場合は注意が必要です。
スコープを抜けないと lock が解放されないため、期待通りに動いてくれません。
- ドキュメント (struct.Stdin.html#method.lock)
- スコープを分けない場合の提出、TLE
- スコープを切った場合の提出、AC
- source を渡す場合の提出、AC
追記: #[fastout]
をつけていると「main()
の終了時に全ての出力をまとめて書き出す」といった挙動になるので、インタラクティブ問題の際には外しましょう。
posted:
last update: