第3回PAST 参加記
無料回だったので参加してみました。
結果
リアルタイム受験をしました。
結果は94点で、エキスパートに認定されました。やったね。
やったぜ#アルゴリズム実技検定 pic.twitter.com/hSCpQq6gqY
— takumi152 (@takumi152) 2020年5月23日
各問題を解くまでの時間はこんな感じでした(開始=13:00、終了=18:00)。
— takumi152 (@takumi152) 2020年6月6日
以下に各問題について書いていきたいと思います。
※どう解いたかうろ覚えな状態で書いているので、説明が雑な部分があります。予めご了承ください
- 結果
- A - ケース・センシティブ
- B - ダイナミック・スコアリング
- C - 等比数列
- D - 電光掲示板
- E - スプリンクラー
- F - 回文行列
- G - グリッド金移動
- H - ハードル走
- I - 行列操作
- J - 回転寿司
- K - コンテナの移動
- L - スーパーマーケット
- M - 行商計画問題
- N - 入れ替えと並び替え
- O - 輪投げ
- 最後に
A - ケース・センシティブ
文字列をそのまま比較した場合と、小文字に変換してから比較した場合を見るだけです。
ちなみにですが、最初はsame
かdifferent
を出せばよいと誤読して1WAを出しました(え?)
B - ダイナミック・スコアリング
序盤の重実装その1。
各参加者がどの問題を解いたかと、各問題の現在の得点を管理すればよいです。
C - 等比数列
ならをそのまま出力するだけです。
なら愚直にやって、答えがを超えたらその時点で計算を打ち切ればよいです。
ループ回数は高々回程度なので、十分間に合います。
D - 電光掲示板
序盤の重実装その2。
サンプルから各数字の見本を持ってきて、見本の状態と現在見ている数字の状態が全て一致しているかどうかを確認しました。
E - スプリンクラー
問題文の通りにグラフを作って解くだけです(雑)
なので、愚直にで解いても十分間に合います。
F - 回文行列
行列の列目と列目の両方に存在する文字があるかどうかを調べればよいです。
set
で出現した文字を管理すれば~程度ですが、愚直に全ての組を調べてもなので間に合うと思います。
G - グリッド金移動
最初の位置からBFSをすれば解くことができます。が、負の座標が存在するので少し厄介です。
自分の場合は、全ての座標を+1000だけずらしてからやることで負の座標を気にする必要が無くなると思ったので、そうしました。
H - ハードル走
各行動を行った後、位置まで到達するのに必要な最小時間をDPで求めればよいです。
、とすれば、遷移式は次のようになります。
(行動1を行った場合)
(行動2を行った場合)
(行動3を行った場合)
ただし、空中にいる状態でゴールする場合(行動2や行動3を行っている途中で位置に到達する場合)があるので、そこの場合分けが必要になります。この場合の遷移式は、
となります。
I - 行列操作
行列の現在の行と列の並び順を管理すればよいです。
また、転置の操作も存在しますが、これは行と列が入れ替わるという操作なので、
転置が行われた回数が偶数の場合は行(または列)に対する操作をそのまま行って、
奇数の場合は行(または列)に対する操作の行と列を入れ替えて操作を行う
とすればよいです。
J - 回転寿司
便宜上、まだ寿司を1つも食べていない子供が食べた寿司のおいしさの最大値をとすると、
- 子供が食べた寿司のおいしさの最大値は、常に降順になっている
という性質があります。
これは、ある子供(とする)が食べた寿司のおいしさの最大値が、その前の子供(とする)が食べた寿司のおいしさの最大値よりも大きいと仮定すると、が食べた寿司の中でおいしさが最大のものはが食べているはずなので矛盾する、という背理法的なやり方で示すことができます(本番ではもちろん無証明で、多分そうだろうなと信じてやりました)。
よって、各寿司が誰に食べられるかは二分探索を利用することで調べることができるので、この問題はで解くことができます。
K - コンテナの移動
各机の一番上にあるコンテナと、各コンテナのひとつ下のコンテナを管理することにすると、問題のサンプルの移動例は以下の画像のように表すことができます。
つまり、このような移動を実現するには、以下の3つの操作を行えばよいです。
移動対象のコンテナが元々あった机の一番上のコンテナを、移動先の机の一番上のコンテナにする。
移動対象のコンテナのひとつ下にコンテナがあれば、それを移動対象が元々あった机の一番上のコンテナにする。
移動対象のコンテナのひとつ下のコンテナを、移動先の机で元々一番上だったコンテナに(あれば)する。
上の図では分かりやすさのために連結リスト風の書き方をしていますが、vector
などで一番上のコンテナとひとつ下のコンテナを管理することにより各移動操作をで行うことができます。よって、この問題はで解くことができます。
L - スーパーマーケット
手前から1番目の商品の中で消費期限の値が最も大きいものがある列(インデックス)は、セグメント木を使うことでで求めることができます。手前から2番目にある商品についても同様です。
よって、手前から1番目と2番目の商品について消費期限の値を調べて、手前から1番目と2番目のどちらから商品が取られたかによって次の商品への入れ替えの場合分けをすればよいです。
M - 行商計画問題
の値の制約が非常に小さいことと、問題名から巡回セールスマン問題のような問題ではないかと予想ができます(本当か?)
実際、この予想は正しくて、個の街の間の最短距離をそれぞれ求めることで、(始点に戻らない)巡回セールスマン問題に帰着することができます。
ただし、制約的にnext_permutation
を利用した解法では間に合わないので、bitDPを利用した解法で解く必要があります。
ところで、この参加記を書いてて気が付きましたが、この問題の英語名は"Real Traveling Salesman"だそうです。やっぱり問題名が答えだった
N - 入れ替えと並び替え
とても難しかった問題。
どのような操作が行われるかを見ると、
隣接する要素のswap
指定した範囲のソート
があります。
愚直にやると任意の範囲に対して複数回ソートを要求されるので当然間に合いません。一方で、ソート以外の操作は、隣接要素のswapしかありません。
そこで、今までに行ったswapの操作に対して、うまく逆の操作を行うことで効率的にソートの操作を行うことができないか・・と、解いていた当時は考えていたと思います(超うろ覚え)
実際、swapのみを行った場合は転倒数は高々にしかならないので、必要な部分だけバブルソートを行ってやれば効率よくソートを行うことができそうです。
では、ソートが必要な部分はどうやって管理するかというと、自分の場合は遅延セグメント木を利用して、「最後にswapが行われてからソートが行われたかどうか」を確認することで行っていました(本番ではこれしか思いつきませんでしたが、実際はset
とかでも十分だと思われます)。
これで十分間に合うだろうと信じて提出をすると、通りました(本番では計算量をちゃんと考えていませんでしたが、実際の計算量はぐらいになりそう?)
O - 輪投げ
今回のPASTで唯一解けなかった問題。WA投げ
DP、もしくは貪欲っぽいなと思ってずっと考えていましたが、想定解は最小コストフローだったそうです。
何を食べたらこの問題がフローの問題だと判別できるんだろうか・・?
最後に
暖色コーダーでも88点が続出していたようでした(当社調べ)が、そんな問題セットでエキスパートを取れたので、かなりの自信になったかなと思います。