こんにちは、ナナオです。

前回に引き続き競プロを実施していきたいと思います。

今回の問題は以下です。

ABC085C - Otoshidama

実装

まずは愚直に実装しました。

はい、全探索ですね。

use proconio::input;

fn main() {
    input! {
        n: u32,
        y: u32,
    }
    for i in 0..=n {
        for j in 0..=n {
            for k in 0..=n {
                if i + j + k == n && 10000 * i + 5000 * j + 1000 * k == y {
                    println!("{} {} {}", i, j, k);
                    return;
                }
            }
        }
    }
    println!("-1 -1 -1");
}

これでも一応一部のテストケースでは通るは通るんですが、nが最大値の2000だった場合、2000^3で実に80億通りも実行しなきゃならないですね。

そうなると実行時間に間に合いません。

というわけで最適化する必要があります。

で、どう最適化するかというところなんですが、、、AIと壁打ちします。

まずは最後のループを消すという方法を試してみます。

これは、つまり10枚のお札があるときに

  • 10000円札が 2枚
  • 5000円札が 3枚

だとわかったら、残りの1000円札は必然的に5枚に決まる、という特徴を活かせば実装できます。

これだけで最大80億のループが4000万にまで減ります。

ということで修正した実装が以下の通りです。

use proconio::input;

fn main() {
    input! {
        n: i32,
        y: i32,
    }
    for i in 0..=n {
        for j in 0..=n {
            let k = n - i - j;
            if k >= 0 && i + j + k == n && 10000 * i + 5000 * j + 1000 * k == y {
                println!("{} {} {}", i, j, k);
                return;
            }
        }
    }
    println!("-1 -1 -1");
}

これで通るようになりました!

ただ、AIの考えたコードはもっとスマートでした。

ということでそのコードをもとに少し修正…。

use proconio::input;

fn main() {
    input! {
        n: u32,
        y: u32,
    }
    for i in 0..=n {
        for j in 0..=(n - i) {
            let k = n - i - j;
            if 10000 * i + 5000 * j + 1000 * k == y {
                println!("{} {} {}", i, j, k);
                return;
            }
        }
    }
    println!("-1 -1 -1");
}

二重目のループの最大値をn - iにすることで、必然的にkを正の整数にするようにしています。

nとyの型もu32で正常動作します。kが負の整数であるかどうかの確認作業が減ったのでいいですね。

ということで今回はこれで👍