1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

はじめに

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

なぜやるのか

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

問題

A - Anyway Takahashi (Difficulty 8)
B - Rectangle Detection (Difficulty 68)
C - Submask (Difficulty 384)
D - Do use hexagon grid (Difficulty 612)
E - Last Rook (Difficulty 1030)
F - Numbered Checker (Difficulty 1601)
G - Reversible Cards 2 (Difficulty 2440)
Ex - Antichain (Difficulty 3178)

前提

この記事にあるコードはこちらを 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 - Anyway Takahashi

問題文そのままですね。
引き算があるので isize で受け取ることに注意です。
usize で受け取ると、 $C-D$ の結果が負になるときに panick します。

  input! {
	a: isize,
    b: isize,
    c: isize,
    d: isize
  }
  println!("{}", (a + b) * (c - d));
  println!("Takahashi");

B - Rectangle Detection

入力でもらった文字の中から四角形の領域を切り取る問題ですね。

考察

出力する $A,B,C,D$ について考えてみます。

$A$ は四角形の上
$B$ は四角形の下
$C$ は四角形の左
$D$ は四角形の右

になります。
ここで、$A$について言い換えると、「上下方向」の「最小値」と言えますね。
他の要素もいいかえると、

$A$ は「上下方向」の「最小値」
$B$ は「上下方向」の「最大値」
$C$ は「左右方向」の「最小値」
$D$ は「左右方向」の「最大値」

となります。上下方向と左右方向に分解して実装できることがわかりました。

実装

$A$ は「上下方向」の「最小値」
について考えます。 (y, x) 座標について、「上下方向」は y ですね。なので、 $A$ は y の最小値を求めます。
ほかの $B,C,D$ についても同様にすると答えがわかります。
a,b,c,d は初期値に注意です。a,c は最小値を求めるので、マスの最大サイズである 10以上の数にして、b,d はマスの最小サイズである 1以下の数にします。

実装はこのようになりました。

  input! {
	mp: [Chars; 10]
  }
  let mut a = 100;
  let mut b = 0;
  let mut c = 100;
  let mut d = 0;
  
  for y in 0..10 {
    for x in 0..10 {
      if mp[y][x] == '#' {
        a = min(a, y + 1); // a は「上下方向」の「最小値」
        b = max(b, y + 1); // b は「上下方向」の「最大値」
        c = min(c, x + 1); // c は「左右方向」の「最小値」
        d = max(d, x + 1); // d は「左右方向」の「最大値」
      }
    }
  }
  
  println!("{} {}", a, b);
  println!("{} {}", c, d);

C - Submask

2進数がでてくる問題です。慣れていないとびっくりするかもしれません。

考察

問題文は複雑ですが、問題文の出力例1 をみてみると考えやすいです。

$N = 1011_{(2)}$ の時の答えは
$0000_{(2)}$、$0001_{(2)}$、$0010_{(2)}$、$0011_{(2)}$、$1000_{(2)}$、$1001_{(2)}$、$1010_{(2)}$、$1011_{(2)}$ ですね。これらの数値は、$N$ の桁をひとつずつ見て、「0 ならば 0」「1ならば0か1」というものになっています。
これはつまり、整数 x について、 if x | N == N を満たせば x は答えになります。計算量を考えると $O(N)$ なのですが、$0 \le N \lt 2^{60}$ なので、実行時間制限に間に合いません。なにか高速化が必要です。

上に書きましたが「1ならば0か1」なので、$N$の桁で1となる位ひとつにつき2通りの答えができます。これに注目します。問題文より、$N$の桁で 1となる位は15個以下ですね。つまり、答えの数値は最大で $2^{15} = 32768$個です。全探索できそうです。
$N$ の桁で 1となる場所を記憶しておいて、それぞれの場所を使うかどうかを探索します。「使うかどうか」は bit全探索が有効です。

計算量を考えると、$N$ の桁で 1 となっている個数を $n$ とすると、答えの数値をひとつ作るのに $O(n)$ かかります。これが $2^n$ 個あるので、全体では $O(n * 2^n)$ になります。 $1 \le n \le 15$ なので、実行時間制限内に解けそうなことがわかります。

実装

実装です。

$N$ の桁で 1 となっている場所を pos に保存します。
mask の桁を見て、 i番目の桁が 1 になっていたら、pos[i] を使用します。
( mask という名前は bit mask からきています。)

1<<sz はシフト演算です。$2^{sz}$ の値が得られます。
mask >> i もシフト演算です。$ mask / 2^i$ (切り捨て) の値が得られます。
if mask >> i & 1 == 1 は、mask を2進数で見たときに右から i 番目の位が 1 かどうかを判定しています。

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

  input! {
    n: usize
  }
  
  let mut pos = vec![];
  for i in 0..60 {
    if n >> i & 1 == 1 { // n の右から i 番目の桁が 1 ならば
      pos.push(i);
    }
  }
  
  let sz = pos.len();
  let mut ans = vec![];

  for mask in 0..1<<sz { // 0 から 2 ^ {sz} まで
    let mut score = 0_u64; // usize は 32bit になることがあるので u64 型で
    for i in 0..sz {
      if mask >> i & 1 == 1 { // mask の右から i 番目の桁が 1 ならば
        score += 1 << pos[i]; // 2 ^ {pos[i]} を足す
      }
    }
    ans.push(score);
  }

  ans.sort();
  for v in ans.into_iter() {
    println!("{}", v);
  }

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

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?