方針
- コーダーにとってわかりやすい
- なるべく愚直な方法で
- 計算時間とメモリ容量を守る
ちょっと順番が前後しますが順に過去に向かっても掘り下げていこうかと思います
公式の解説はこちら
https://img.atcoder.jp/abc099/editorial.pdf
数字までprint
しなきゃいけないかと思ってたら違いました。 問題文良く読めという話
を出力すれば良いので解法は正直に考えればinput()
を int
に変換して大小を比較する方法でしょうか。str
のまま受け取って len(str)
で比較しても良いですけど……
1 | input_num = int(input()) |
与えられた2つの「雪より出てる部分の高さ」から、雪の積もった高さが等しいことを利用すると、
2つの塔の高さの差がわかります。すると、例えば1+2+3+4
1+2+3+4+5
となる2つの塔の差が5
になる というように例を考えていけば、(低い方の塔の高さ) = 1+2+...+(塔の高さの差 - 1)
となります
さて、塔の高さが求まればあとは簡単な話で(雪の深さ) + (塔の雪から出ている部分) = (塔の高さ)
を低い方か高い方、どちらかの塔に対して計算すれば良いです。今回は低い方で考えています
1 | def delta2lower_height(arg_num): |
こんな銀行預けたくない さて、考えていきましょう……
6^n 円引き出す時を考えると、6^n 円 を引き出す動作 * 6回は、明らかに
6*6^n = 6^(n+1) となるため、6^(n+1) 円 1回 を引き出す動作と金額が等しくなり、
5回分を無駄に使うことになってしまいます。よって、
6^n 円を引き出す動作はすべての n∈(自然数) に対して5回までと制限することができます。
同様のことは9^n 円についても言えます (8回まで)
また、問題条件より額が100000円を下回ることから、
よって、引き出しの動作としては以下のものが挙げられます
引き出し全体としては、これらの引き出しが0回以上組み合わされると考えられます。
ところで、6^n 円の引き出し系統の回数が定められた時、
残る動作は 9^n 円の引き出しと1円の引き出しのため、最適な引き出し方法が一意に定まります。
よって、6^1… 6^6 円 について 0回以上5回以下 = 6 * 6 * 6 パターンについて調べる方法で
十分に短い時間で全ての(最適な)組み合わせについて調べることができます。
1 | input_num = int(input()) |
1 | import copy |
ABC099 の提出記録です。実行時間や使用メモリもわかります。銀行恨むぞ
https://beta.atcoder.jp/contests/abc099/submissions?f.Task=&f.Language=&f.Status=&f.User=nuekodory
【公式リンク】図表とざっくりとした解説はこちらからどうぞ。
https://img.atcoder.jp/arc099/editorial.pdf
高橋君が命令文をもぐもぐ食べます。
1 | orders = input() |
まずこの問題のいうS(n)
を返す関数を作ってみましょう。
各桁の数字を足し合わせて返せばいいわけですね。
ところで “各桁の数字を1つずつ切り出す” 方法にはいくつかあります。
一番pythonic なのはこんなのですかね:
https://stackoverflow.com/questions/14939953/sum-the-digits-of-a-number-python1
2
3
4
5
6
7
8# written by Uli Köhler on stackoverflow.com
def sum_digits(n):
s = 0
while n:
s += n % 10
n //= 10
return s
もちろんこれでも良いのですが、iterable
でくるくる回す方が簡単に思いつく(気がする)ので、
文字列str(number)
を for
で回して1文字ずつ文字(chr
) を切り出します。
切り出した文字はint()
で囲ってやると数字に戻ります。
1 | def sum_of_digits(arg: int) -> int: |
さて、あとは”割り切れる”かどうかを判定すれば良いです。これは剰余(a % b
)が0であることと等価です。
また、条件より入力N
は正の整数なのでゼロ除算は考えなくて良いです。
1 | def sum_of_digits(arg: int) -> int: |
さて公式の解説にもある通り、数列中の最小値が1であることから、
何回動作を繰り返しても数列の要素の最小値は1です。
よって全ての数字を揃えた時、必ず1に揃うことになります。
(わからなかったら手を動かして実際に動作を紙に書いて実行してみましょう)
さて、全て1に揃えればいいと気づいたところで、再び動作をよく観察すると、
1回の動作につき “1になる要素数” が最大で K-1
個できるのがわかります。
右にずらしたり左にずらしたりして試行してみると、最善の動作1回に対して
常にK-1
個の要素が1になるとわかります
最小値に揃える連続する要素の数K
試行回数 T
に対し、要素が1に等しい要素数 E
とすると、
最善の動作を繰り返したとき E = 1 + T * (K-1) ただしE<=N
よって、 N =< 1 + T * (K-1)
になるT
を求めれば良いとわかります。
数列の並びが入力されますが捨てて良いです。
1 | input_line = input().split() |
……とりあえずグラフでも描いて考えてみましょうか!(なげやり)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import matplotlib.pyplot as plt
def sunuke_sum(arg: str) -> int:
sum_digit = 0
for char in arg:
sum_digit += int(char)
return sum_digit
sunuke_list = []
for i in range(1, 1000):
sunuke_list.append(i / sunuke_sum(str(i)))
plt.plot(sunuke_list)
plt.ylabel("number/sunuke(number)")
plt.show()
するとこんな感じで出てきます
グラフパッとみた感じ、
1, 2, 3, 4, 5, 6, 7, 8, 9, 19, 29, 39, 49, 59, 69, 79, 89, 99, 199, 299, 399…
…499, 599, 699, 799, 899, 999, 1999, 2999, 3999…
って見えますね!見えますよね!残念でした!そうじゃないんです
この通り、 1099, 1199, 1299, 1399… も「すぬけ値」なんですね、不可解
ところで、ランダムな数字を考えてみます(ここで爽健美茶のJANコードが登場)
4902102119450
さて、この数字から n/S(n)
について考えてみると、
1 | 4899999999999 < 4902102119450 |
よって明らかに、(4899999999999 / S(4899999999999) < 4902102119450 / S(4902102119450)
となる数4899999999999
が存在します。
これの存在条件について考えてみると、
となりますので、すぬけ数の候補は全体からこれを除いたものになります。よって
N * (10 ** D) + (10 ** D - 1)
ただし 1 <= N <= 99, 1 <= D <= 14
がすぬけ数の候補です。 (厳密にはこの中には上の条件を 満たす ものがありますが、計算しても差し支えないので計算します)
これら候補の数は限られているので、これに対して n/S(n)
を算出し比較します。
便宜のため、10 ** 15 - 1 = 999999999999999
はすぬけ数であるとします。
{すぬけ数の候補: n/S(n)}
となる辞書 sunuke_dict
を作成し、大きい数から比較してn/S(n)
が小さいもので更新された時、このn
をすぬけ数としてsunuke_list
に追加します。
するとsunuke_list
にすぬけ数が大きなものから順に列挙されるので、順序を逆にしてから先頭から取り出していけば答えになります。
1 | def sunuke_sum(arg): |
ちょっと考えつかなかった。解説をよく読んでやっと理解できました……
さて、条件をよく読んでみると、「高」群、「橋」群の二つについて(以下 T, H とします)
さて、ここで群を形成するための作業を
という作業に置き換えてみると、これについての補グラフを考えると、
となります。よって、
という動作を行えば良いことになります。
「たがいに結合のない群の振り分け」を考える時、グラフを連結成分分解
(つまり孤 isolated である集団ごとに分ける)すれば良く、
都市についてこれを求めてみます
1 | road_dict = {1: [2, 4], 2: [1], 4:[1]} # 道の結合を示す |
ARC099 の提出記録です。実行時間や使用メモリもわかります。まあよくやったよ、うん
https://beta.atcoder.jp/contests/arc099/submissions?f.Task=&f.Language=&f.Status=&f.User=nuekodory
図表とざっくりとした解説はこちらからどうぞ。
https://img.atcoder.jp/abc100/editorial.pdf
図をよーく見れば、1人が9切れ以上取ることができないことが分かります。
隣接しない = 最低でも1つ間隔を開けなければならない
ということは、8切れ取った時1つ間隔で取ることになりこれが最大です。
全部で16個から8個ずつ取っても問題ないので、全体のケーキの残りについては考える必要がなく、1人最大8切れを守れば良いことになります。
(マイナスの入力は条件から存在しません)
よって、A, B が8以下であることが Yay!
の条件ですので、
1 | if A <= 8 and B <= 8: |
となるように設計すれば良いことになります。
1 | input_line = input().split() |
色々な解法がありますが、ここでは D = 0 or 1 or 2
ということを利用して解きます(場合分け)
“100でちょうど0回割り切れる” == “100で割り切れない”
と考えると解けるかと思います。
コードにすると1
2if number % 100 != 0:
do
ということですね。
ここで N < 100 であれば単純に答えが N で終わりなのですが、N <= 100
なので N == 100
について分ける必要があります。
愚直に N == 100
を場合分けしても良いですし、
100の倍数について無視してカウントするようにしても良いです(今回はこちらを採用)
“100でちょうど1回割り切れる” == “100で割り切れて、10000で割り切れない”
よってD == 0 の時と同様に N == 100 についてのみ場合分けをするか、
10000の倍数を無視してカウントするようにすれば良いです(今回はこちらを採用)
“100でちょうど2回割り切れる” == “10000で割り切れて、1000000で割り切れない”
よってD == 0, 1 の時と同様に N == 100 についてのみ場合分けをするか、
1000000の倍数を無視してカウントするようにすれば良いです(今回はこちらを採用)
1 | input_line = input().split() |
“3倍する” or “2で割り切る” の操作をするのですが、
“3倍する” のはまあ何回でもできますよね。
というわけでネックになるのは “2で割り切る” ことです。
“2で割り切る” 動作は数列に対して 1回の操作で1つ以上適用しなければならないので、
1回の操作で1つを “2で割り切る” と考えます。
するとこの問題は “2で何回割り切れるか” という問題になりますので、
数列のそれぞれの数について “2で何回割り切れるか” を調べます。
いくつか方法はありますが、2進法に変換して0が続く桁数を調べるのが良いでしょう(多分)
1 | array_length = int(input()) |
全ての場合についてスコアを出そうとすると、ケーキ1000個から500個を取り出すときに計算量が最大で、このとき
C(1000, 500) = 2.702882409E+299
よってこんなんでfor
を回すと帰ってこない(計算が終了しない)ので別の方法を考えます。
ところで、スコアは絶対値を採用するわけですが、
絶対値というのは max(number, -number)
と等価です。
よってこの場合、単純にスコアを足していったあとのことを考えると、
「綺麗さ」「おいしさ」「人気度」の数値が beauty, yummy, popular
だったとして1
2
3
4
5
6
7
8+ beauty + yummy + popular
+ beauty + yummy - popular
+ beauty - yummy + popular
- beauty + yummy + popular
- beauty - yummy + popular
+ beauty - yummy - popular
- beauty + yummy - popular
- beauty - yummy - popular
の8パターンのうち、最大のものが1
abs(beauty) + abs(yummy) + abs(popular)
と等しいとわかります。
すると、絶対値で考えなくても単純に足していって最後にmax
を出せば良いことがわかります。
さて、ここまできたところでもう一度上の8例について見てみます。
+ beauty - yummy + popular
で考えてみましょう。
これを最大化させるためには + beauty - yummy + popular
を各ケーキに対して算出し、大きいものから順番にケーキの個数だけ取れば良いことに気がつくでしょうか。
すると8パターン全てに対して同じことが言えるのですから、これを計算してmax
を取ればスコアが最大であると言えます。
1 | input_line = input().split() |
ABC100 の提出記録です。実行時間や使用メモリもわかります
https://beta.atcoder.jp/contests/abc100/submissions?f.Task=&f.Language=&f.Status=&f.User=nuekodory