LoginSignup
6
1

More than 3 years have passed since last update.

【AtCoder解説】PythonでABC182のA,B,C問題を制する!

Posted at

AtCoder Beginner Contest 182A,B,C問題を、Python3でなるべく丁寧に解説していきます。

ただ解けるだけの方法ではなく、次の3つのポイントを満たす解法を解説することを目指しています。

  • シンプル:余計なことを考えずに済む
  • 実装が楽:ミスやバグが減ってうれしい
  • 時間がかからない:パフォが上がって、後の問題に残せる時間が増える

ご質問・ご指摘はコメントツイッターまでどうぞ!
Twitter: u2dayo

目次

ABC182 まとめ
A問題『twiblr』
B問題『Almost GCD』
C問題『To 3』

ABC182 まとめ

全提出人数: 7497人

パフォーマンス

パフォ AC 点数 時間 順位(Rated内)
200 AB---- 300 37分 5814(5649)位
400 ABC--- 600 96分 4832(4668)位
600 ABC--- 600 44分 3991(3829)位
800 ABCD-- 1000 100分 3118(2956)位
1000 ABCD-- 1000 50分 2306(2144)位
1200 ABCDE- 1500 96分 1631(1475)位
1400 ABCDE- 1500 67分 1117(965)位
1600 ABCDE- 1500 50分 744(596)位
1800 ABCDE- 1500 37分 483(341)位
2000 ABCDE- 1500 27分 304(181)位
2200 ABCDEF 2100 97分 185(88)位
2400 ABCDEF 2100 80分 110(40)位

色別の正解率

人数 A B C D E F
3040 99.1 % 69.9 % 42.3 % 12.5 % 2.9 % 0.0 %
1461 99.5 % 97.7 % 87.7 % 51.8 % 12.7 % 0.1 %
1122 99.5 % 99.5 % 97.2 % 87.5 % 50.7 % 0.6 %
692 100.0 % 100.0 % 98.8 % 97.7 % 86.6 % 3.6 %
357 100.0 % 100.0 % 99.2 % 98.9 % 97.2 % 24.4 %
134 96.3 % 96.3 % 95.5 % 96.3 % 91.8 % 59.7 %
27 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 77.8 %
7 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 %

表示レート、灰に初参加者は含めず

簡易解説


A問題 (7436人AC)『twiblr』

算数です。

B問題 (6249人AC)『Almost GCD』

$A_i$ は最大 $1000$ です。 $1000$ は $1001$ 以上の数では絶対に割り切れません。
$2$ ~ $1000$ がそれぞれいくつ割り切れるか実際に試して、最大の数字を出力すればいいです。

C問題 (5077人AC)『To 3』

$N$ は $10^{18}$ 未満です。つまり、最大 $18$ 桁しかありません。
桁の消し方は、 最大 $18$ 桁 それぞれについて、消すか消さないかの $2$ 通りあります。
$2^{18}$ 通り はだいたい $26$万 通りなので、すべての桁の消し方を試して、3の倍数になるか判定すればいいです。
いわゆる bit全探索 というアルゴリズムです。
全部消して $0$ にすることは許されていないので、その場合は $-1$ を出力することに注意してください。

D問題 (3419人AC)『Wandering』[この記事では解説しません]

累積和を使います。紙に書いて考えるとわかるかもしれません。

E問題 (1988人AC)『Akari』[この記事では解説しません]

最大 $50$ 万個の電球全部について、明かりが届くマスを調べていてはとても間に合いません。
「この先は既に照らされていることがわかりきっている」時点で打ち切れば、計算量が $O(HW+N+M)$ になって間に合います。

F問題 (233人AC)『Valid payments』[この記事では解説しません]

動的計画法らしいです。

私(うにだよ)の結果(参考)

screenshot.351.png

D問題をもうちょっと落ち着いて考えたほうがよかったと思います。

A問題『twiblr』

問題ページA - twiblr
コーダー正解率:99.1 %
コーダー正解率:99.5 %
コーダー正解率:99.5 %

考察

単純な算数ですが、勢いでペナルティをもらうともったいないので、少し立ち止まって確認するといいかもしれません。

コード

A, B = map(int, input().split())
print(2 * A + 100 - B)

B問題『Almost GCD』

問題ページB - Almost GCD
コーダー正解率:69.9 %
コーダー正解率:97.7 %
コーダー正解率:99.5 %

考察

ある正の整数 $K$ があるとします。$K$ は、$K$ よりも大きい数では絶対に割り切れません。例えば、 $6$ は $7$ 以上の数では絶対に割り切れません。

この問題の数列の最大値は $1000$ に設定されているので、$1001$ 以上の数は 与えられた数字を $1$ つも割り切れないことが確定しています。

答えの候補である$2$ から $1000$ まで、数列をいくつ割り切れるか全部試しても間に合います。

コード

N = int(input())
A = list(map(int, input().split()))

ans = 0
current_max = 0

for x in range(2, 1001):
    score = 0  # 割り切れた数(GCD度)です
    for a in A:
        if a % x == 0:
            score += 1
    if score > current_max:
        ans = x
        current_max = score

print(ans)

C問題『To 3』

問題ページC - To 3
コーダー正解率:42.3 %
コーダー正解率:87.7 %
コーダー正解率:97.2 %

考察

数学的な考察が必要な問題に見えますが、実は 「ありえる桁の消し方を全探索して判定する」と工夫せずに解けます。

$N$ の制約は、$10^{18}$ 未満です。最大で $18$ 桁しかありません。各桁について、消すか消さないかの $2$ 通りあります。よって、桁の消し方は最大で $2^{18}$ 通りあります。$2^{18} = 262,144$ です。全部試しても間に合います。

(全部の桁を消す $1$ 通りは許されていないので、本当は $2^{18}-1=262,143$ ですが、簡単のため含めています)

実装

『使う・使わない』の2通りは『bit全探索』

この問題のような『使う・使わない』の $2$ 通りを全探索するときは、いわゆる『bit全探索』という方法を使います。

bit全探索の詳細は省きます。(本当は詳しく書きたいのですが、bit全探索が出るたびに詳しく書くのはつらいので、そのうちどこかにまとめるかもしれません)

pythonでは、bit全探索はitertoolsモジュールのproductを使うと楽です。

from itertools import product
product((True, False), repeat=l)

こう書くと、長さ $l$ で、各要素がTrueFalseどちらでできる組み合わせがもれなく全て生成されます。

3の倍数の判定法

「数字を消したあとくっつけて整数に戻して、$3$ で割り切れるか判定する」のは、明らかに面倒くさそうです。

そこで、「ある整数 $N$ が $3$ の倍数」⇔「$N$ の各桁の数字の合計が $3$ の倍数」であることを利用すると、楽に実装できます。

(例:$18 → 1 + 8 = 9$ 、 $762 → 7 + 6 + 2 = 15$ )

具体的には、$N$ を $1$ 桁ごとに分解したリストを作ります。例えば、$1234567$ なら、[1,2,3,4,5,6,7]にします。ある桁を消さずに使うなら桁和に足して、消すなら足しません。そして、最終的に桁和が $3$ の倍数かどうか判定すればいいです。

全部消すのはダメです

桁を全部消すと桁和が $0$ になるので、必ず $3$ の倍数になります。しかし、この問題では許されていません。

わざわざ除外するのは面倒なので、答えが初期値のままなら $-1$ を出力すればいいです。

コード

from itertools import product

# とりあえず文字列Sで受け取ったあと、桁ごとの数字のリストAにすると扱いやすいです
# N = 6227384 -> A = [6,2,2,7,3,8,4]

S = input()
A = [int(x) for x in S]

l = len(S)  # Sの桁数です(Sは文字列で受け取ったので、len(S)を使えます)
ans = l

for bits in product((True, False), repeat=l):
    digit_sum = 0  # 消してない桁の和です
    score = 0  # 桁を消した数で、少ないほうがうれしいです
    for i, bit in enumerate(bits):
        if bit:
            # 上からi桁目を消さずに使います
            digit_sum += A[i]
        else:
            # 上からi桁目を消します
            score += 1

    if digit_sum % 3 == 0:
        #  3の倍数になる消し方なら、最小値を更新します
        ans = min(ans, score)

if ans == l:
    # 3の倍数にならなかったので、-1を出力します
    print(-1)
else:
    print(ans)
6
1
2

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