LoginSignup
3
1

More than 3 years have passed since last update.

AtCoder Beginners Selection (ABS) をRustで解いてみた

Last updated at Posted at 2019-10-06

もはや何番煎じか分かりませんが。。。

入力関数(read1, readn)

fn read1<T: std::str::FromStr>() -> T {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim().parse().ok().unwrap()
}

fn readn<T: std::str::FromStr>(delimiter: &str) -> Vec<T> {
    let s = read1::<String>();
    s.split(delimiter).map(|e| e.parse().ok().unwrap()).collect::<Vec<T>>()
}

以下の回答では、 main() の前に上記のコードを書いています。

read1() :1行の標準入力を型 T の値として返す
readn(delimiter) :1行の標準入力を delimiter で区切り、Vec<T> として返す

解答

PracticeA - Welcome to AtCoder

fn main() {
    let a = read1::<i32>();
    let bc = readn::<i32>(" ");
    let b = &bc[0];
    let c = &bc[1];
    let s = read1::<String>();
    println!("{} {}", a+b+c, s);
}

やるだけ。

ABC086A - Product

fn main() {
    let ab = readn::<i32>(" ");
    let a = &ab[0];
    let b = &ab[1];
    if a * b % 2 == 0 {
        println!("Even");
    } else {
        println!("Odd");
    }
}

はい。

ABC081A - Placing Marbles

fn main() {
    let s1s2s3 = read1::<String>();
    let mut ans = 0;
    for i in 0..3 {
        let s: i32 = s1s2s3[i..i+1].parse().ok().unwrap();
        ans += s;
    }
    println!("{}", ans);
}

文字列として受け取って、1個ずつ整数にして足す。

ABC081B - Shift only

fn main() {
    let N = read1::<u32>();
    let mut A = readn::<u32>(" ");
    let mut cnt = 0;
    'solve:
    loop {
        for i in 0..N {
            if A[i as usize] % 2 == 0 {
                A[i as usize] /= 2;
            } else {
                break 'solve;
            }
        }
        cnt += 1;
     }
    println!("{}", cnt);
}

シミュレーション。1個ずつ2で割っていって、行けるとこまで回数を数える。 Rust は 'solve: で示した部分のように、多重ループの中で break によって脱出する先を指定できるのが非常に便利。

ABC087B - Coins

fn main() {
    let a = read1::<u32>();
    let b = read1::<u32>();
    let c = read1::<u32>();
    let x = read1::<u32>();

    let mut ans = 0;

    for a_num in 0..a+1 {
        for b_num in 0..b+1 {
            for c_num in 0..c+1 {
                if 500 * a_num + 100 * b_num + 50 * c_num == x {
                    ans += 1;
                }
            }
        }
    }
    println!("{}", ans);
}

単純に全探索する。

ABC083B - Some Sums

fn main() {
    let nab = readn::<i32>(" ");
    let n = nab[0];
    let a = nab[1];
    let b = nab[2];

    let mut ans = 0;

    for i in 1..n+1 {
        let mut num = i;
        let mut sum = 0;
        loop {
            if num == 0 {
                break;
            }
            sum += num % 10;
            num /= 10;
        }
        if sum >= a && sum <= b {
            ans += i;
        }
    }
    println!("{}", ans);
}

1 から N まで N 通りの num を全探索。 num の1の位を抽出→10で割る→1の位を抽出→…を、各 num が 0 になるまで繰り返す。

ABC088B - Card Game for Two

fn main() {
    let n = read1::<i32>();
    let mut a = readn::<i32>(" ");
    let mut alice = 0;
    let mut bob = 0;

    if n % 2 == 1 {
        a.push(0);
    }
    a.sort();
    for i in 0..(n+1)/2 {
        bob += a[(2 * i) as usize];
        alice += a[(2 * i + 1) as usize];
    }

    println!("{}", alice - bob);
}

カードをソートして、大きい方から交互に取っていく。 N の値が奇数のときは Bob の取る枚数が一枚少なくなるが、はじめから 0 のカードを1枚追加しておけば OK 。

ABC085B - Kagami Mochi

use std::collections::HashSet;

fn main() {
    let mut uniq = HashSet::new();

    let n = read1::<u32>();
    for _ in 0..n {
        uniq.insert(read1::<u32>());
    }
    println!("{}", uniq.len());
}

(鏡餅の段数)=(unique な直径の鏡餅の枚数)。 HashSet を使えば一発。

ABC085C - Otoshidama

fn main() {
    let ny = readn::<i32>(" ");
    let n = ny[0];
    let y = ny[1];

    let yukichi_max = y/10000;
    if yukichi_max > n {
        println!("-1 -1 -1");
    } else {
        let mut flag = true;
        'solve:
        for i in 0..yukichi_max+1 {
            let ichiyou_max = (y - 10000 * i) / 5000;
            if ichiyou_max > n - i {
                continue;
            } else {
                for j in 0..ichiyou_max+1 {
                    if 10000 * i + 5000 * j + 1000 * (n - i - j) == y {
                        println!("{} {} {}", i, j, n - i - j);
                        flag = false;
                        break 'solve
                    }
                }
            }
        }
        if flag {
            println!("-1 -1 -1");
        }
    }
}

TLE に注意。最高額紙幣(一万円札、または一万円札の枚数を固定した状態での五千円札)をフルに使っても金額 Y に達しないなら、その時点で不可能。一万円札と五千円札の枚数が決まれば、自動的に千円札の枚数が決まるので、千円札に関しては全探索する必要がない。

ABC049C - 白昼夢 / Daydream

その1

fn main() {
    let s = read1::<String>();
    let length = s.len();
    let mut pos = 0;
    let mut flag = 0;

    while pos < length {
        if pos+7 <= length && s[pos..pos+7] == "dreamer".to_string() {
            pos += 7;
            flag = 2;
        } else if pos+6 <= length && s[pos..pos+6] == "eraser".to_string() {
            pos += 6;
            flag = 1;
        } else if pos+5 <= length && s[pos..pos+5] == "dream".to_string() {
            pos += 5;
            flag = 0;
        } else if pos+5 <= length && s[pos..pos+5] == "erase".to_string() {
            pos += 5;
            flag = 0;
        } else if flag > 0 {
            pos -= flag;
            flag = 0;
        } else {
            break;
        }
    }
    if pos == length {
        println!("YES");
    } else {
        println!("NO");
    }
}

文字列の前から、取れる限り長い文字列を取る。取りすぎたせいで後の文字列が構成不能になっているなら、取りすぎた分を戻す。
(dreamerase → "dreamer" ase → "dream" erase → "dream" "erase" のように、取りすぎた "er" を戻す)
戻してもどうしようもない場合は不可能。

その2

fn main() {
    let s = read1::<String>();
    let length = s.len();
    let mut pos = length;

    while pos > 0 {
        if pos >= 7 && s[pos-7..pos] == "dreamer".to_string() {
            pos -= 7;
        } else if pos >= 6 && s[pos-6..pos] == "eraser".to_string() {
            pos -= 6;
        } else if pos >= 5 && s[pos-5..pos] == "dream".to_string() {
            pos -= 5;
        } else if pos >= 5 && s[pos-5..pos] == "erase".to_string() {
            pos -= 5;
        } else {
            break;
        }
    }
    if pos == 0 {
        println!("YES");
    } else {
        println!("NO");
    }
}

単語のお尻に付いている "er" だけが問題なので、お尻から調べていけば戻す必要がない。

ABC086C - Traveling

fn main() {
    let mut v: Vec<Vec<i32>> = vec!(vec!(0, 0, 0));
    let n = read1::<u32>();
    for _ in 0..n {
        v.push(readn::<i32>(" "));
    }

    let mut flag = true;
    for i in 0..n {
        let dt = (v[i as usize][0] - v[(i+1) as usize][0]).abs();
        let dx = (v[i as usize][1] - v[(i+1) as usize][1]).abs();
        let dy = (v[i as usize][2] - v[(i+1) as usize][2]).abs();
        if dx + dy > dt || (dt - (dx + dy)) % 2 != 0 {
            flag = false;
            break;
        }
    }
    if flag {
        println!("Yes");
    } else {
        println!("No");
    }
}

同じところを行ったり来たりすれば、2手分の時間をつぶせる。すなわち、旅行計画が破綻するのは、移動時間が足りない or 移動時間の余りが奇数のとき。

まとめ

Rust はいいぞ。

3
1
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
3
1