キーエンス プログラミング コンテスト 2021参加記(~C問題)
300-400(1)-500-0(1)-0-0で1ペナ3完1200点。
A - Two Sequences 2
全ての組を見るとかかりTLEする。
を求めるには、までの要素を見ればよい。さらにを求める際、数列からを選ぶか選ばないかで2通りに場合分けできる。
- を選ばない場合
このとき、という制約から、も選ぶことができない。つまりどちらの数列からも番目までの要素から選ぶことになり、この答えはに一致する。
- を選ぶ場合
このとき、からまでの要素のどれを選んでもよい。正の数同士の乗算なので、貪欲に最大値を選ぶのが最適である。
まとめると、からまでの要素の最大値をとおいて、
が答えとなる。一つ前の答えが必要なので前から順に計算していく。
計算量は、。
B - Mex Boxes
数列の中に、があるときこれらをすべて使って点を得られる。この時、もの中に含まれているなら、とすることで点得られる。一般に、点得るには個の数が必要なので、最高で点得ることが可能なら貪欲に個の数を使って点得た方が良い。
あとは、数列のなかにそれぞれの数が何個含まれているかをカウントし、数が大きい方から見て貪欲に作っていく。最高で回までしか得点を得られないことに気を付ける(1WA)。
計算量は、。
C - Robot on Grid
全てのマスに文字が書き込まれているなら、のDPで解くことができる。配るDPで書くと、
dp[i][j]:=(マス(i, j)に行く経路の数)
と定義して、マス(i, j)が右方向に進めるならdp[i][j+1]+=dp[i][j]
とすればよい。下方向も同じくdp[i+1][j]+=dp[i][j]
となる。最初のマス(1, 1)にいるロボットは体なので、dp[1][1]=1
である。
ここで、とおく(何も書かれていないマスの数を表す)。マスへの書き込み方は通りあるので、マス(1, 1)に体のロボットをおいて、すべてのロボットが異なる動きをすると仮定して考える。このとき、何も書かれていないマスに到達したロボットのうち、は「R」、は「D」、は「X」であり、右に進めるのはマスに「R」か「X」が書かれていた場合のみである。よって、動的計画法の遷移は
dp[i][j+1]+=dp[i][j] * 2 / 3
となる。下に進む場合も同様である。計算時は、常にMODを取って計算することを忘れないようにする。また、割り算のMODは逆元を用いて計算できる。
計算量は、。
感想
パァン!
👺< 実装が遅い!
AtCoder Beginner Contest 188参加記(~D問題)
100-200-300-0(2)-0(1)-0で0ペナ3完600点。
A - Three-Point Shot
とのうち小さい方にを足して比較すると良い。もともとどちらが小さかったかを工夫して持つ必要がある。自分は、必ずとなるように適切にswapしてから問題を解いた。こうすると、もともと小さかった方は必ずである、といえる。
計算量は、。
B - Orthogonality
ベクトルとか内積とか、その辺の言葉を知らなくても問題文に定義が書いてあるのでそれをやるだけ。ただし、判定すべき式の左辺に「」が含まれているので、その辺の処理を工夫する必要がある。総和記号を使って書くと、
の真偽を調べればよい。これは、for文などで実装できる。
足し算や掛け算が出てきたときは、数値のオーバーフローに気を付ける(最大値を考える)。今回の場合、最大値はなので、32bitのint型でも収まる。優しい…(伏線)
計算量は、。
C - ABC Tournament
問題文が難しいシリーズ。要するに番号の小さい順に人ずつマッチさせて戦わせ、勝ったほうだけ残せということ(最後の人だけは負けた方を出力しなければならないが、勝った方の逆を出力するという解釈をすればよい)。これを愚直にシミュレーションしたときの計算量を考える。
回戦目では、ペアが試合を行う。一方が勝ってもう一方が負けるので、回戦目に出場する人数は回戦目のとなる。このとき、シミュレーションするために必要なメモリへの参照回数を計算すると
となる。よって、シミュレーションにかかる計算量はであるので、シミュレーションするだけで時間内に答えが得られる。
ちなみに、公式解説の解法2には「選手の集合を前半と後半で2つに分けたとき、一番レートが高い選手と逆の集合にいる選手のうち一番レートが高い選手が答え」と書いてあった。トーナメント表を実際に書いてみると理解できる。こちらも、計算量はと変わらないが、実装はかなり楽である。
D - Snuke Prime
問題文に明記されていないが、制約より日目から日目まで考えればよい(無限ではない)。
また、それぞれの日に関して、個あるサービスのうちどれを使うかはすでに決められていて、選べるのは「利用するすべてのサービスの利用料を普通に支払うか」「すぬけプライムで支払うか」の2択である。この選択は日ごとに独立で、最終的な支払金額を最小化したいので、この2択のうち安い方を貪欲に選べばよい。利用するすべてのサービスの利用料を普通に支払った場合の金額は、いもす法を使って効率的に求められる。
ただし1日ずつ考えていると時間が足りない。与えられる情報は高々個であることから、座標圧縮を用いることで計算量をに落とすことができる。ただし、オーバーフローに注意する(「日数×サービス数×利用料」の最大値がとなってしまう)。どちらの支払い方法でも、支払う日数は変わらないので、1日当たりの支払い金額で比較してから日数を掛ければよい。オーバーフローに気付かず、2WA。
計算量は、ではなく、座標圧縮するときにソートしているので!!!
E - Peddler
町を頂点、道を辺としたグラフで考えると良さそう。あとはわからん。
F - +1-1x2
AGCで見た
感想
オーバーフローには気を付けよう!
AtCoder Beginner Contest 187参加記(~E問題)
100-200-300-400(2)-0-0で2ペナ4完1000点。茶パフォ。
ちなみに、コンテスト後15分くらいでE問題を通した。コンテスト中に通せよ!!!
A - Large Digits
もも桁であるので、桁の数としてではなくつの数字として入力を受け取ると処理が楽に書ける。各桁の数字を足して、大小比較をすればよい。ちゃっかり出力制約にのときはを出力せよと書かれているが、もも同じ値なので特に注意しなくてもACできる。(解説書いてて今気づいた)
計算量は、がともに桁の整数だったとして、。
B - Gentle Pairs
とりあえず、を満たす整数の組を列挙する。こういう条件が付いてるときの2重for文をスルスル書けるようになると楽。
それぞれの整数の組に対して、「直線の傾き」の値を調べる。直線の傾きはで求められるので、この値が以上以下かどうか調べればよい。割り算するときは、
- 0除算していないか
- 演算結果は整数で持つのか小数で持つのか
あたりに気を付ければよい。今回は、0除算する可能性があるので、こういう例外は割り算する前に処理しておく。ならば2点が縦に並んでいるということなので、傾きは無限大となる(条件を満たさない)。また、演算結果は小数で持つべきである。
以上のことに気を付けながら条件を満たす組をカウントする。
計算量は、。
C - 1-SAT
不満な文字列が存在するとしたら、与えられた個の文字列のどれかに一致する。つまり、不満な文字列の候補を一つずつ調べていき、実際にその文字列が不満な文字列の条件を満たすか調べればよい。ただし、候補は最大で個あるため、判定は効率よく行う必要がある。
不満な文字列の候補に対し、の先頭に!
を付けた文字列が、与えられた個の文字列の中に含まれていれば、は不満な文字列である。ここで、文字列を辞書順でソートしておくことにより、文字列の集合に対しても二分探索が使えるので、この文字列の検索は一回あたりで判定できる。
あとは不満な文字列が見つかるまでループを回せばよい。
計算量は、。
D - Choose Me
まず当然の話として、高橋氏が全く演説を行わなかった場合は必ず青木氏に負け、高橋氏がすべての町で演説を行った場合は必ず青木氏に勝つ。さらに、個の町を適切に選んで演説を行ったとき青木氏に勝てるなら、個の町で演説を行っても青木氏に勝てる。このことより、解の二分探索で解けそうだと目星を付ける。
問題は個の町をどのように選ぶかであるが、これは「演説を行ったときの効率が良い」町から順に貪欲に選んでいけばよい。この、「効率の良さ」をどのように算出するかが重要である。
仮に町で演説を行ったとすると、青木氏の得票数はされ、高橋氏の得票数はされるので、両者の得票数の差は開くことになる。よって、各町をで降順ソートして、先頭から順に演説すれば最善である。
個の町で演説したときの実際の得票数を計算するのに、「番目から番目までの青木派・高橋派のそれぞれの有権者数」を使うが、これは累積和を使うことで高速に求められる。よって一回の判定はでできる。
計算量について、ソートに、累積和を求めるための前計算に、解の二分探索にかかる。よって、全体ではの計算量となる。
ソートするための基準を適当に決めて2WA。罠にまんまと引っかかった。
E - Through Path
初めに少しだけ愚痴。問題文に書かれているクエリの説明が簡素すぎだと思う。せめてが辺番号を示しているということは明記してほしかった。
愚直に毎クエリごとに木を走査して頂点を更新していくと間に合わないので、最後に木の根から走査して動的計画法で更新することを考える。
クエリを読み解くと、スタート地点となる頂点と通ってはいけない頂点は必ず1本の辺で繋がっていることがわかる。よって、木の根を適当に決めたとき、スタート地点から到達できる部分木に根が含まれているパターンと含まれていないパターンの2つに分類できる。
木の根が部分木に含まれるとき、木の根にする。ただし、走査しているときに辺を通過したらして打ち消す。
木の根が部分木に含まれないとき、はじめは木の根に何も足さない。操作しているときに辺を通過したら、そこからスタート地点となる頂点を含む部分木なので、する。
こうすることで、1回の走査だけですべての頂点の値を更新することができる。
計算量は、。
F - Close Group
読んでない
感想
成績は散々だったけど、考察の大まかな方針は間違えていなかったのでそこは評価したい。もっと考察力を上げるにはやっぱり精進するしかないのかな…。
パナソニックプログラミングコンテスト(AtCoder Beginner Contest 186)参加記(~E問題)
100-200-300(1)-400-500-0で1ペナ5完1500点。
B - Blocks on Grid
ブロックに対してできる操作は、「ブロックを取り除く」のみである。この操作でブロックの個数が増えることはないので、操作後のブロックの個数はすべてのマスの中で一番少ないものに合わせる必要がある。
よって、マス目を全探索してブロックの個数の最小値を保持し、各マスにあるブロックの個数との差の総和を求めればよい。
計算量は、。
C - Unlucky 7
公式解説とは違うオーバーキル気味な方法で通した。
前計算として、「進数で表すとを含む」「進数で表すとを含む」数を列挙する。再帰関数で幅優先探索を実装し、が含まれる数のみを配列に放り込んでsort->unique->eraseすると、を含む数が昇順で並ぶ。あとは与えられた以下の数の個数を、配列の二分探索を用いて求めればよい。
計算量ってどれくらいなんだろう?幅優先探索で生成されるノードの数は進数の場合個なんだけど、これを数式でを使ってきれいに書けないな…。くらいということにしておく。
[追記]ソートしているので計算量はでした。ソートの存在忘れがち
A進数をB進数に変換する関数を暇なときに組もうと思った(暇なときにやる気が起きるはずがないので永遠に実装されない)。
D - Sum of difference
絶対値記号が邪魔なので配列をソートして考える。さらに引かれる数と引く数ごとにわけて考えると、それぞれの整数が足される回数は配列の添え字番号に依存していることがわかる。
具体的には、
が答え。
計算量は、。
E - Throne
拡張ユークリッドの互除法で解いた。
玉座を番目の椅子と考えると、
を満たす最小のが解となる。少し変形すると
となる。これは、拡張ユークリッドの互除法を用いて
を満たすを求める問題に落とせる。
計算量は、。
F - Rook on Grid
解けなかった。縦→横と動くパターンと、横→縦と動くパターンで分けて考えた後重複するマスを引く方針で考えたが、上手くいかずタイムアップ。もっと形式的にできるっぽい。
感想
C問題とE問題を解くのが遅かった。特にC問題は単純実装でも通る問題だったので、オーバーキル解法をゴリゴリ実装する前にもっと簡単に記述できる解法はないか考えるべきだった。
積の魔方陣は構成できるか?
※この記事は苫小牧高専アドベントカレンダー2020 10日目の記事です。
9日目の記事はまだ上がっていないようです。高専という感じがして好きですよ。
というわけで10日目はみみねこが担当します。Twitterをやっているので是非フォローしてください。
→みみねこ (@3_3_nk) | Twitter
高専といえば勉強ですね!勉強といえば数学!というわけで今日は数学の話をします。
目次
- はじめに:魔方陣とは
- 積魔方陣を定義する
- 和魔方陣から積魔方陣を構成する
- サイズの小さい積魔方陣を構成したい
- 素因数が2つ存在する積魔方陣
- ラテン方陣とオイラー方陣について
- 各要素が互いに素な集合を探す
- 他の次元の積魔方陣も構成してみる
- 結論
- 課題
はじめに:魔方陣とは
魔方陣って知ってますか?知らない人はWikipediaを読んでください。
簡単に言うと、異なる数字を正方形状に並べたとき、その各行・各列(・各対角線)の和がそれぞれ等しくなるようなものを魔方陣といいます。縦横にそれぞれ 個の正整数が並んでいる魔方陣を 次魔方陣と呼ぶことにします。
次魔方陣は次のようなものが考えられます。縦横に並ぶ数の合計はどこも となっていますね。
和魔方陣から積魔方陣を構成する
最初に説明した魔方陣を「和魔方陣」と呼ぶことにします。
実は、 次の和魔方陣が一つ求まっていれば、そこから 次の積魔方陣を構成することができます。とりあえず構成アルゴリズムを示します。
- 元となる和魔方陣の各要素を としたとき、各要素を と置き換える
次積魔方陣の構成方法を具体的に示すとこうです。
なぜこの方法で積魔方陣が構成できるかは一目瞭然ですね。 のべき乗の掛け算は、指数部分の足し算として計算できるからです。
以上より、この方法を使えば「 次以上の積魔方陣は存在し、実際に和魔方陣から積魔方陣を構成できる」ということが証明できました!「積の魔方陣は構成できるか?」というタイトルは回収したので、これ以降は数学が好きな人だけ見てください。
サイズの小さい積魔方陣を構成したい
先ほどの方法ならいくらでも積の魔方陣を生成できます。しかし のべき乗しか使えないので、積魔方陣の次数が増えていくほど使われる整数もどんどん大きくなっていきます。もっと"サイズの小さい"積の魔方陣はないか、考えてみましょう。
ここで、魔方陣の大きさを比較するために「積の魔方陣のサイズ」をある一列の数の総積で定義します。たとえば、先ほどの方法で生成した 次の積魔方陣のサイズは となります。このサイズがより小さくなるような積魔方陣を構成したいです。すぐに思いつくのは、すべての要素を で割る方法です。
この場合サイズは となりますが、まだまだ小さくなりそうです。もっと別のアプローチで積魔方陣を構成できないでしょうか?
素因数が2つ存在する積魔方陣
積魔方陣は整数の掛け算に関する問題なので、各要素を素因数分解して考えるとよさそうです。先ほどまでの方法だと各要素の素因数は だけでしたが、それ以外の素因数が含まれる場合を考えてみます。
積魔方陣に素因数が2種類含まれる場合、それぞれの素因数は互いに素なので、その素因数ごとに積の魔方陣を分解できます。たとえば、各要素が と表せる積の魔方陣が存在すると仮定すると、各要素が である積魔方陣と である積魔方陣に分解して考えることができます。分解した後の積魔方陣は「どの2つの数も異なる」という条件を満たしていなくても構いません(合成した後に条件を満たしていれば十分)。条件が緩いので、分解してできる二つの積魔方陣のことは「準積魔方陣」と呼び分けることにしましょう。
素因数が1種類だけの積魔方陣の生成方法は先ほど説明しました。これを使って、実際に 型の 次積魔方陣を一つ構成してみましょう。
作成したい積魔方陣には 種類の相異なる正整数が必要です。 型の準積魔方陣と 型の準積魔方陣に登場する正整数の種類をそれぞれ 種類ずつとすると、二つの準積魔方陣をうまく組み合わせることで合成後の積魔方陣に 種類の相異なる正整数を生成することが可能です。(行列の掛け算と違って、単純に同じ位置にある要素同士を掛け合わせています。)
ちょっと見づらいので、指数部分だけ抜き出して書いてみます。数字の並びがうまくずれているおかげで、合成後の指数の組に重複が無いというところに注目してみてください。
この方法で生成された積魔方陣は以下の通りで、そのサイズは となります。
初めに生成したものと比べると、だいぶサイズが小さくなってきました!見た目もシンプルになってきましたね。ただ、 のべき乗と のべき乗しか使用していないので、積魔方陣の次数 が大きくなるとやはり指数的にサイズが大きくなってしまいます。どうやら、もっとサイズを小さくできそうですので、もう少し議論を続けることにしましょう。
ラテン方陣とオイラー方陣について
先ほどの方法で出てきた「二つの準積魔方陣を"うまく"組み合わせる」というあいまいな部分を少し詳しく見てみましょう。
合成する前の準積魔方陣のうち、指数部分のみを抜き出してできる方陣のような、「 から までの整数が各列・各行に重複なく並ぶ」方陣のことをラテン方陣といいます。
ラテン方陣の例:など
また、ラテン方陣を二つ合成して、各要素が整数のペアであるような方陣を作ります。このとき、どの 要素も異なるようなものをオイラー方陣といいます。
ここで、先ほどまでの議論より、ある正整数 に対して 次のオイラー方陣が存在すれば、サイズの小さい 次の積魔方陣を構成できると言えます。
話がそれるので証明や具体的な構成方法は省きますが、 の時を除いて 次のオイラー方陣が存在することがわかっています。つまり、 次と 次以外の積魔方陣は、オイラー方陣を用いる方法で構成することができるのです*1!
各要素が互いに素な集合を探す
ところで、合成する前の二つの準積魔方陣の各要素は、それぞれ 型と 型でした。このように異なる素因数で分解した理由は、二つの準積魔方陣の要素を掛け合わせた結果がすべて異なるように設定したからでしたね。つまり、二つの準積魔方陣に登場するどの2要素を取ってきても、互いに素になるように設定したということが言えます。
ここで、二つの準積魔方陣を と名付け、それぞれの 次準積魔方陣に登場する要素の集合を とします。集合 の要素数は に等しくなります。
例を挙げましょう。 次の二つの準積魔方陣 を
と定義すると、準積魔方陣の各要素の集合 は以下のようになります。
確かに、集合 の要素と集合 の要素は互いに素であることがわかります。さらに、準積魔方陣 を合成してできる積魔方陣のサイズは、集合 の要素の総積と等しくなります(いままでの図を見れば理解できるはずです)。つまり、集合 の要素である正整数の値が小さくなればなるほど、完成する積魔方陣のサイズも小さくなるということです。
ここで、二つの集合の要素を眺めてみると、集合 の要素のうち が比較的大きいことに気が付きます。集合 の全要素と互いに素となる正整数のうち、まだ使われていない最小のものは です。なんと、集合 の要素を と置換しても、積魔方陣が正しく構成でき、サイズを小さくすることが可能です!
この方法で生成された積魔方陣は以下の通りとなります。サイズは で、先ほどの積魔方陣よりさらにサイズが小さくなっていることがわかります!
他の次元の積魔方陣も構成してみる
今までの議論のおさらいとして、他の次元の積魔方陣を構成してみます。
1次の積魔方陣
次の方陣とはつまり、整数が 個しか使われていない方陣です。なので当然、各列・各行の積は等しくなります。よって、すべての 次方陣は積魔方陣であるといえます!
次の最小の積魔方陣は以下の通りで、そのサイズは です。
2次の積魔方陣
次の積魔方陣は存在しません。証明を記します。
【証明】
と表せる 次の積魔方陣が存在すると仮定します。積魔方陣の定義より、以下の等式が成り立ちます。
より両辺を で割って、 が得られます。しかしこれは、積魔方陣の定義である「どの2つの数も異なる」に矛盾します。よって仮定が誤りで、 次の積魔方陣は存在しません。
4次の積魔方陣
オイラー方陣を用いて構成してみます。準積魔方陣 を構成する要素の集合 の値をどう決めるかが重要です。とりあえず、集合 に のべき乗を、集合 に のべき乗をそれぞれ入れてみましょう。集合 の要素数は なので、小さい順に並べます。
明らかに集合 の値が大きいですね。集合 には素因数 が含まれているので、集合 には の倍数を含むことができません。 の倍数でない正整数、つまり奇数は必ず集合 の全ての要素と互いに素になります。よって、集合 は最終的に次のようになります。
これらの集合の各要素を 次のオイラー方陣に適用させると、 次の積魔方陣が完成します。
この積魔方陣のサイズは です。この積魔方陣が 次の積魔方陣の中で最小のサイズである証明はできませんでしたが、おそらく最小と思われます。
5次の積魔方陣
先ほどと同様にして、 次の積魔方陣を構成してみます。集合 の要素は のべき乗、集合 の要素は奇数、と仮定すると以下のようになります。
よくみると、今度は集合 の要素のうち が大きく見えます。ここで、「素数は自分自身の倍数を除いてどんな整数とも必ず互いに素となる」という性質を使うと、と置き換えることができますね。よって、最終的な集合 は次のようになります。
これらの集合をもとに積魔方陣を構成すると、以下のようになります。
この積魔方陣のサイズは となります。 次の積魔方陣が 桁以下の整数だけで構成されている美しい姿には、もはや感動すら覚えます。素晴らしい…
課題
この中では議論できなかった課題を並べておきます。「これ解けたよ!」ってのがあったら教えてください。
- 次の最小サイズの積魔方陣は求められるでしょうか?
- 集合 の適切な要素の選び方はあるでしょうか? もし素数のリストが与えられている場合に、効率よく構成することはできないでしょうか?
- さらっと条件から外していましたが、今回の議論では方陣の各対角線の積は考慮しませんでした。各対角線の積も等しくするためには、今回紹介したオイラー方陣を用いる構成方法は使えません。どんな方法が考えられるでしょうか?
- 今回は数学の問題として手作業で構成することを想定していましたが、もし 次の最小の積魔方陣を構成するプログラムを組むとしたらどんなアルゴリズムが考えられるでしょうか? そのアルゴリズムの計算量はどうなるでしょうか?
以上です。ここまでで約10000文字らしいです!400字詰めのレポートが25枚書けます。高専生の皆さんは、ちゃんとレポートを書きましょうね!!!
苫小牧高専アドベントカレンダー、明日はあやさんの担当です。それでは!
AtCoder Regular Contest 109参加記(~C問題)
300-400-500-0(2)-0-0でノーペナ3完1200点。
A - Hands
この問題の構造は、各階→頂点、廊下・階段→辺として重み付きグラフに落とし込める。このグラフの2頂点間の最短路問題を解けばよい。例えば、ダイクストラ法で実装すれば頂点数、辺数より計算量はとなる。
実際に解いた時はなぜかBFSで実装していたがACできた。この辺よくわかってないのだが、最短路問題はDFSやBFSでも正しく解けるのだろうか?(今日はもう疲れたので明日考える)
B - log
長さがの丸太は、
- 長さの丸太そのまま
- 長さの丸太を切る
のどちらかの方法でしか入手できない。さらに、もし長さの丸太から長さの丸太を切り出せば、長さの丸太に関して同じ議論が続くこととなる。この時、長さがの丸太が大量発生してもったいないので、基本的には
- 長さの丸太は、長さの丸太を買って手に入れる
ことにする。
ただし、これだと長さの丸太を有効活用できていない。どの丸太も同じ値段なのだから、はじめに長さの丸太を買って、短い丸太から順に切り出せるだけ切り出すのが最適である。
ここで、長さの丸太から何本の丸太を切り出せるか?という小問題が出てくる。これは、本切り出せると仮定して二分探索を行う*1ことでで計算可能。からまでの整数の総和はで求められる。
本買う予定だったうちの本を買わずに、長さがの丸太本で代用するので、答えはである。計算量は、。
C - Large RPS Tournament
トーナメントの参加者は最大でとなるため、愚直にシミュレーションしていては当然間に合わない。ここで、各参加者が出す手は周期的であるため、同じ計算をしている箇所があるはずである。トーナメントの1回戦目を考えると、人ごとに同じ手を出すペアが同じ順番で登場することがわかる。よって、はじめの人分だけ考えてそれを長さの配列に保存すれば一回戦目の結果はすべてわかる。
その次の試合結果も、先ほどの議論を再帰的に適用することで、はじめの人分だけ考えればすべての結果がわかる。
これを回繰り返したとき、配列の先頭に保存されている手が優勝する手である。
計算量は、。
D - L
解けなかった。
(諸事情により削除しました)
上記ツイートの数字は、石がそのマスまで移動するのにかかる最小手数を表している。移動したい位置がくの字であることは保証されているので、移動先の3つのマスにそれぞれ移動するまでの最小手数(上記ツイートの数字)を計算し、3つの最小手数の中の最大値に対して
- 最大値が3つ中1つだけならその最大値を出力
- 最大値が3つ中2つあるなら最大値+1を出力
というアルゴリズムで実装したが、WAだった。
感想
3完はできたものの、もう少し早くACできればもっと良かったかなと思う。ただペナルティを出さなかったのはえらい。
*1:オーバーフローを避けるために、実装時はから初めて2倍にしていく方法でやった。
AtCoder Beginner Contest 184参加記(全問題)
100-200-300-0-0(1)-600(1)で1ペナ4完1200点。
A - Determinant
行列は丸括弧で与えないとダメなんじゃないの?と思いつつ解いた。ちなみに丸括弧にはいろいろな意味があるので、紛らわしさをなくすために行列は角括弧で表すことがあるらしい。ためになったね~(もう中学生)
問題文に書かれている通り、入力値から行列式の値を計算して出力する。いわゆる「問題文に答えが書いてある」のもっとも基礎的なパターンで、なんと一番早いACは13秒である。おそろしや…
計算量は、。
C - Super Ryuma
ぱっと見、どこから手を付けていいのか迷ってしまった。この駒の移動方法は、
- 「対角線上のマスへ移動」
- 「マンハッタン距離が3以下のマスへ移動」
の2種類の移動が組み合わさっている。ここで、「対角線上のマスへ移動」は距離に制限がないので、この移動を2回繰り返せば、移動したいマスのすぐ近くまでは絶対にいけることがわかる。この辺をしっかり議論していく。
まず、スタート地点とゴール地点が重なっている可能性がある。この場合は手で移動できる。
次に、手で移動できる場合を考える。これは、問題文で示された駒の動ける範囲にゴール地点が重なっている場合のみであり、この条件を満たすかは問題文に示されたとおりの式を満たすかどうかでわかる。
よって、これら以外のマスに行くためには最低手必要である。ここで、先ほどの2種類の移動方法の選び方により、手移動する方法は以下の3通り考えられる。
- 「対角線上のマスへ移動」してから「対角線上のマスへ移動」する
- 「対角線上のマスへ移動」してから「マンハッタン距離が3以下のマスへ移動」する
- 「マンハッタン距離が3以下のマスへ移動」してから「マンハッタン距離が3以下のマスへ移動」する
1番の移動方法では、以下に示すように市松模様の同じ色のマスへしか移動できない。逆に、市松模様の同じ色のマス同士なら、必ず2手で移動できる。同じ色のマスであるための条件は、座標と座標の和の偶奇が一致していることである。
違う色同士のマスへ移動するには、移動したいマスの1つ隣のマスを選んで2手かけて移動し、3手目に1マス隣に移動することで移動可能である。このことから、最大でも3手以内に移動可能であることがわかった。つまり、これ以外での移動方法に関して議論するとき、2手で移動できないことがわかれば十分である(2手で移動できないならこの方法で3手かけて移動するのが最短だから)。
2番の移動方法について考える。この場合、ゴール地点とのマンハッタン距離が3以下であるようなマスへ1手で移動できるか?ということを考えればよい。斜めに移動するとき、ゴール地点とのマンハッタン距離が最短となるように移動するには、例えばゴール地点の座標と移動先の座標が等しくなるように移動すればよい。(証明略)
あとは、移動先のマスとゴール地点のマスのマンハッタン距離が3以下かどうか判定すれば移動できるかがわかる。
3番の移動方法についてもしっかり考えないといけない。この移動方法は、「マンハッタン距離が6以下のマスへ移動」することと同じである。よって、スタート地点とゴール地点のマンハッタン距離が4以上6以下なら、2手で移動できる。
ここまでの条件をすべて確認して、ようやくACが得られる。しかし、どうやら3番の移動方法を考慮していない解法でもACが得られてしまうようだ。このテストケースが抜けているのもどうかと思うが、嘘解法で通してしまった人はせめてこの理屈だけでも理解するようにすべきだと思う。
計算量は、。
D - increment of coins
解けなかった。とりあえず、
金貨が枚、銀貨が枚、銅貨が枚ある状態へ遷移する確率
というDPを思いつく。制約からもが通るのであってるかなーと思ったが、確率を浮動小数点数型で保存すると誤差がひどすぎて正しい答えが得られず、かといって遷移する通り数を保存するとオーバーフローしてしまう。Pythonで書こうか迷って順位表を見たらE問題よりF問題の方が解かれていて、捨てた。
公式の解説を見る限りでは数値誤差でひっかけるつもりはなさそうなので、単純に考察が間違っていただけかもしれない。たぶんそうだと思う。順位表を見て正解だった。
E - Third Avenue
解けなかった。F問題を解いた後に問題を読んだが、言い訳をすると時間が足りなかった。
問題自体はよくあるグリッド上の迷路の最短距離を求めるもので、テレポーターによる瞬間移動をどう実装するかが肝である。BFSをベースに実装し、テレポーターがあるマスを事前にメモしておくことですぐテレポート先を参照できるようにした。また、同じ種類のテレポーターを2回以上使う必要はないので、1度使った同じ種類のテレポーターは使えないようにした。
これであっていると思って提出したのだが、2ケースだけWAが出ている。原因はわからない。
F - Programming Contest
これはいわゆる「部分和問題」というやつで、集合の中からいくつか整数を取ってきたときの和に関する問題をまとめてこう呼ぶ。解法もいろいろあって、制約から決めることが多い(諸説あり)。
全探索して解いたときの計算量は、個ある整数それぞれに対して、「取る」か「取らない」かを決めていくので、選び方は通り。このうち以下で最大のものを探すだけなので、計算量はとなる。今回の制約の最大値を代入して概算すると、となるため実行時間制限に間に合わない。
ここで、半分全列挙と呼ばれるテクを使うことで、計算量をにすることができる。詳しいやり方はググるべし。
ざっくり方針を述べる。40個の整数を20個ずつに分けてそれぞれの選び方をすべて試す。前半の集合から得られた部分和をとして、後半の集合の部分和の中から以下のうち最大のものを二分探索で探す。これらの最大値が答えとなる。
計算量は、整数の選び方をすべて試すのに、二分探索するのにかかるので、全体ではとなる。
感想
点数で重みをつけるなら問題間の難易度の勾配はしっかりつけてほしい。もっとACL活用するような問題出してもええんやで…