/ 最近 .rdf 追記 編集 設定 本棚

脳log[20200826] 第二回全国統一プログラミング王決定戦予選 - AtCoder/C 問題 - Swaps (再) | 第二回全国統一プログラミング王決定戦予選 - AtCoder/D 問題 Shortest Path on a Line



2020年08月26日 (水) [Ruby] 当然あるだろうと、先にタイプしてから存在を確認するメソッドに Numeric#sign がある。ところが 2.7 にもないのである。zero?, nonzero?, positive?, negative? は存在している。でも -1,0,+1 のいずれかを返す sign メソッドはないのである。レシーバが -0.0,+0.0 の場合に何を返すべきかはすぐにはわからないけど、-1.0,+1.0 を返すのは罠になりそうなのでレシーバ自身が無難だろう。■「sign メソッド」で検索したらトップに出た>「Math.Sign メソッド (System) | Microsoft Docs」。ほらほら。■■■わかったぞ。i <=> 0 で代用できる。メソッドであってほしいものと演算子で十分なものと、なんかちぐはぐだね。■<=> (Spaceship Operator) についてビャーネさんが何か言いたそうにしていたのをどこかで読んだ。Ruby での存在意義はこの1メソッドを定義するだけでクラスを Comparable にできることだと認めてはいる。でも C++ では何を追加しても、互換性を保って追加をする限り、煩雑さを増すことにしかならないだろう。■Uniform Function Call が一番楽しみだな、C++ に導入されるとしたら。シンタックスの統一はテンプレートの適用を拡大するし、メンバ変数を使用しないアクセサリメソッドをグルーピングのためだけにメンバ関数にするような暴挙を阻止できる。

最終更新: 2021-10-24T17:45+0900

[AtCoder] 第二回全国統一プログラミング王決定戦予選 - AtCoderC 問題 - Swaps ()

読んだ眺めた>「競プロerのための群論 (swapと順列と対称群) - little star's memory

数学の用語で何か抽象的なことを言ってるなーということと、Swaps と Moving Piece の2問(だけじゃないけど)が取り上げられているということはわかった。

Moving Piece は先日解いたので(20200820p01)、以前解けなかった(20191111p01) Swaps も解ける気がした。

 提出 #16248329 (AC / 403 Byte / 283 ms)

もちろん今日も AC に至るまでに WA を出した。それも前回と全く同じ入力に対して同じように誤った答えを出した。前回書いたスクリプトはひとつも参考にしなかったにも関わらず、構成も結果も瓜二つなのは、書いた人間が同じだからですね。同じところに留まっている……。

 デバッグ

前回と違ったのはテストケースが利用できたこと>「atcoder_testcases > nikkeiqual_2019 > C

今回のような Yes/No 問題の場合、間違った方法ででたらめな答えが出ても2分の1の確率で AC になってしまいデバッグが捗らない。そのような場合に(テストケースなしでも)使える手法をひとつ思い付いた。

 提出 #16245274 (TLE)

スクリプトの真ん中に sleep (※引数なしなら永眠)を仕込んで、前半部分の Yes/No 判断に誤りがないかを確かめた。結果は TLE と AC のみだったので、前半部の判断は間違っていない。

 提出 #16245473 (WA)

予想外の WA (TLE なし)だった。これは後半部の No を sleep に置き換えたものなのだけど、1つも TLE がなかった。1つもないというのは(入力とバグり方がコラボした)偶然の結果なのだけど、偶然でもなんでも無条件 Yes は明らかなバグだ。

こんな感じで TLE(sleep) や RE(ヌルポ、0除算、変数名タイポ)が Yes/No ではない第3、第4の答えとしてデバッグに利用できると思った。こういう(アナーキーな)考え方ってゴルファーが得意としてそうだよね。常識だと思ってそう(違うんですよ)。

 バグ

わかってみれば些細なことで、思えば去年もインデックスの扱いに確信が持てずに試行錯誤をしていた。どうして B 数列が予めソート済みではないという、そのひと手間で穴にはまるのか、何度でも。

つまり、A 数列の初期配列と B 数列の初期配列。A 数列のソート済み配列と B 数列のソート済み配列。A, B 両者の扱いが対等なこれら4つは脳みその中に居場所が確保されていた。しかし、B 数列の初期配列をソート済みとする、と条件を整えたときに A 数列の初期配列がどうなるか(ソート済みではないし、元の初期配列とも異なる)、という概念が脳みそからすっぽり抜け落ちていた。A, B の対称部分に気持ちを良くして、差異に向ける目がなかった。去年も、今回も(初めは)。

 去年の借りを返す 提出 #16278383 (AC / 445 Byte / 199 ms)

「去年の WA」を完成させたもの。必要以上に慎重だった(見極めが甘く無駄だった)二分探索がないぶん、冒頭の AC 提出より速い。

 考察(おまけ)

前回の日記に全部書いてある(あれで全部だった)。ひとつだけ付け加えるなら、「逆の例は、B 数列に重複する値が存在する場合や、B 数列の最小要素以下の要素が A 数列に複数ある場合など」の「など」でごまかした具体例の3つ目。

ソート済みの B 数列に異なる値を持つ隣接要素 B[i] と B[i+1] があって、B[i] < A[k] <= B[i+1] となる A[k] が存在しないときも、A 数列のすべての要素にあるべき位置が存在するとは言えなくなる。(A 数列がソート済みなら B[i] < A[i+1] を確かめるだけでいい)

最終更新: 2021-08-15T23:54+0900

[AtCoder] 第二回全国統一プログラミング王決定戦予選 - AtCoderD 問題 Shortest Path on a Line

余勢を駆って前回2つの WA であっさり引き下がっていた D 問題に再挑戦した。これも C 問題と同じ 600 点問題。

 提出 #16254983 (AC / 387 Byte / 402 ms)

実は区間の片端に着目した貪欲法で解けるんですよ、というのが目から鱗だったスケジューリング問題そのままだった。どこにそう書いてあったかは忘れた*

前回の WA 提出 #8424473 を見ると、今と同じことは考えていたことがわかる。C 問題の場合にも言えるけど、そこで結果を分けたものが何か。考えたことを過不足なく言い換えることと、バグなくコードに置き換えること。それができるかどうか。

それはどうやったらできるようになるんですか? という問いは、どうしてそこで間違えたんですか? という問いと対になる。わかりませんよ。ワーキングメモリが足りないんじゃね?(テキトー) こういうとき脳筋は手を動かして慣れるしかない。そうすればより少ない脳のリソースで解けるようになったり、型通りの手法で解けるようになったりして、うっかりや見落としで間違えることがなくなる(という期待)。

 去年の借りを返す 提出 #16288240 (AC / 319 Byte / 375 ms)

区間のどちら端に着目するか。冒頭の AC 提出では L のソート順に処理していたが、「前回の WA」では R でソートしていた。それを完成させてみたら、冒頭の AC 提出で使用していた Array#slice! と Array#insert という、配列に対して呼び出すにはやや気が引けるメソッドが、Array#pop と Array#push という配列に相応しいメソッドに置き換わっていた。二分探索も3回から2回に減っている。Swaps の場合もそうだったけど、AC に至りさえするなら部分的には過去の方が優れてるのなんでだろう。

 解説記事を読んでもわからないので自分で書く。

グラフとか最短経路とかコスト0の辺を張るとかわからへんねん。

R でソートするバージョン(#p02.02)。

  1. RC を、ある地点(R)に到達する最低コスト(C)を記録したリストとする。R が同じならコストの低い方を記録する。R についても C についても昇順(2要素の比較において R が大きければ C も大きい)。リストは最初空で、R の昇順に伸長していくこととする。
  2. 区間 [L,R] から R までをコスト C で繋ぐ場合を考える。
    1. RC を二分探索し、最初に L かそれより後ろに到達する要素を見つける。より遠くに到達する要素はより高コストなので「最初」を見つける。

      見つからない場合は断絶があるということでありパスする。R の昇順に RC に要素を追加しているのであり、今後 [L,R) の区間に到達する辺は現れない。R に到達する辺があとから追加されることはあるが、C が負ではないのでパスで良い。

    2. 見つけた要素のコストを加算して C′とする。辺を追加することによりコスト C′で R に到達できることになる(始点は問わない)。
    3. RC に記録されている C′より高コストの要素は用済みであり取り除く(RC がコストの昇順であることを利用する)。処理時点で R が最遠到達地点であり、それより近くに高コストで到達することに価値がないから。
    4. ペア [R,C′] に記録する価値があるなら RC の末尾に追加する。
  3. ということの繰り返し。

ひょっとしたらこれも DP (動的計画法) の一種かもしれないけど、わからんけど、自分が頑なに DP の用語を使わないのは、それを言ってもメリットがないから。

一行ずつ左から処理するにあたり保持するデータを vs = [0]*4 と定めたあとは、特に詰まるところはなかった。つまりそこで詰まったということであり、一番のお楽しみポイントだったということ。あるマスにおける状態と、状態から状態への遷移が、4要素の配列でまかなえることの発見が。

これもそう。DP の核心は何を記録して遷移するかであり、それがわからないのに、「あ、これ DP だ」ということを言っても問題が解けない。むしろそれを言うことで何かわかったつもりになることが目眩ましになって問題に集中できない。過去に何度かそういう失敗をして、DP だということは言わないことにした。dp という変数名も自分にとって何も説明していないので使わない。

DP の一語でなく、配る DP、集める DP まで区別できるとまた違うのかもしれないけど、自分はそれらを識別しない。

 @2021-08-15 ARC026-C 蛍光灯 が同じ問題だった。

どちらがどちらと同じと言うかはまあいいや。

 提出 #25085547 (AC / 295 Byte / 269 ms)

速いでそ>「Ruby によるすべての提出」 それ以前に提出が少なすぎる……。

* 蟻本(初版第1刷)の43ページ「区間スケジューリング問題」だった。

See also...

listed by...