LoginSignup
0
0

More than 1 year has passed since last update.

はじめに

AtCoder Beginner Contest 293 の C問題までを Rust で解きました!
詳しめに解説をしていこうと思います。
役にたちましたらいいねいただけると嬉しいです。
AC 確認はしてますが、もっとスマートな書き方がありましたら教えてください。

なぜやるのか

プログラミングは知っているけれどアルゴリズムは知らない、という方に向けて書いています。半分は社内向けです。
また、私は Rust に慣れておらず、その練習のためにやっています。
(コンテスト本番には C++ を使って参加している水色コーダーです。)

問題

A - Swap Odd and Even (Difficulty 12)
B - Call the ID Number (Difficulty 65)
C - Make Takahashi Happy (Difficulty 431)
D - Tying Rope (Difficulty 830)
E - Geometric Progression (Difficulty 1453)
F - Zero or One (Difficulty 2038)
G - Triple Index (Difficulty 1753)
Ex - Optimal Path Decomposition (Difficulty 2831)

前提

この記事にあるコードはこちらを import しています。

#[allow(unused_imports)]
use proconio::{input, fastout, marker::Chars, marker::Usize1};
#[allow(unused_imports)]
use std::collections::{HashSet, HashMap, BTreeSet, VecDeque};
#[allow(unused_imports)]
use std::cmp::{max, min};
#[allow(unused_imports)]
use itertools::Itertools;

A - Swap Odd and Even

入力された文字列を入れ替える問題ですね。

入れ替え方が独特で、

  • 1文字目と2文字目
  • 3文字目と4文字目
  • ...
    という入れ替え方をします。

入力を mut でもらったあと、 swap を使って入れ替えることで実装できました。

    input! {
        mut s: Chars
    };

    for i in 0..s.len() / 2 {
        s.swap(i * 2, i * 2 + 1);
    }
    println!("{}", s.into_iter().join(""));

B - Call the ID Number

ちょっと複雑な問題ですね。
実際にシミュレーションをしてみれば問題概要がわかるかと思います。

また、この問題は効率的な解き方が浮かびにくいかもしれませんが、そういうときは愚直にシミュレーションをすると解けたりします。
実際にこの問題もシミュレーションで解きます。

ひとりずつ順番に見ていけばシミュレーションができるので、計算量は $O(N)$ になります。十分間に合います。

コードはこのようになりました。

    input! {
        n: usize,
        a: [Usize1; n]
    };

    // 1 だったら呼ばれていない
    let mut res = vec![1; n];

    for i in 0..n {
        // 呼ばれていなければ
        if res[i] == 1 {
            // a[i]番目 の人を呼ぶ
            res[a[i]] = 0;
        }
    }

    // 呼ばれていない人数
    println!("{}", res.iter().sum::<usize>());

    // 呼ばれていない人の番号
    for i in 0..n {
        if res[i] == 1 {
            println!("{}", i + 1);
        }
    }

C - Make Takahashi Happy

グリッド上を動きながら、同時に数字を管理する問題ですね。
AtCoder ではこのようなグリッドを扱う問題はよく出ます。

計算量を考えてみます。まずは全探索から。
取り得るルートを列挙してみると、 $H = 10, W = 10$ の時に最大となって、 $C(H + W - 2, W - 1) = C(18, 9) = 48620$ 通りです。
今回の問題はそれに加えて整数の重複判定もあるのですが、これに $O((H + W)^2)$ かかるとして、 $C(18, 9) * 20^2 = 19448000$ ステップです。
実際は重複判定の部分が $(H + W)^2$ よりも小さくなるので、全探索で十分に間に合う計算量になりました。

続いて実装です。グリッドでの全探索は DFS や BFS をよく使います。今回も例に漏れずDFSを使います。
DFS といえば再帰ですね。もしくは whilevec を使っても実装できます。今回は再帰でやりました。

whilevec を使うときは、ループ部分でこのようにするとスマートです

while let Some(val) = vec.pop() {
  ...
}

コードはこのようになりました。

fn dfs(y: usize, x: usize, mp: &Vec<Vec<usize>>, stack: &mut Vec<usize>) -> usize {
    // 枝刈り
    // 見たことある数字だったら return
    if stack.contains(&mp[y][x]) {
        return 0;
    }

    let h = mp.len();
    let w = mp[0].len();

    // 端まで来た
    if y == h - 1 && x == w - 1 {
        return 1;
    }

    // いまのグリッドの整数を stack にいれる
    stack.push(mp[y][x]);
    let mut ret = 0;

    // 下
    if y < h - 1 {
        ret += dfs(y + 1, x, mp, stack);
    }

    // 右
    if x < w - 1 {
        ret += dfs(y, x + 1, mp, stack);
    }

    // いまのグリッドから抜けるときに、
    // この回で stack に入れた整数 (mp[y][x]) を抜く
    stack.pop();
    ret
}

#[fastout]
fn main() {
    input! {
      h: usize,
      w: usize,
      mp: [[usize; w]; h]
    };

    let mut stack = vec![];
    println!("{}", dfs(0, 0, &mp, &mut stack));
}

最後まで読んでいただきありがとうございます!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0