Upgrade to Pro — share decks privately, control downloads, hide ads and more …

競プロにおける「実験コード」の書き方

physics0523
May 01, 2022
2k

 競プロにおける「実験コード」の書き方

競技プログラミングにおける「実験コード」の書き方について、自分なりにどのような点に気を遣って書いているかをまとめたスライドです。

(このスライドは「競プロ道場鯖」にて 2022/04/30 に行われたLTで使用されたものです。)

配布資料はこちら : https://drive.google.com/file/d/1Po6310ABSxUVf1csmIJWNztnmqhEo677/view?usp=sharing

physics0523

May 01, 2022
Tweet

Transcript

  1. 実験コードを書くべきケース(2) ・構築 / 復元付き問題 ・いくつかの解を眺めると、パターンが見えてくるものも ・ Yes / No 判定問題

    ・多くの結果の観察によって、必要十分条件を導く ・数え上げ ・必要十分条件の導出は数え上げにも有効 ・答えを数表として眺めると、規則性がある場合も ・最適化 ・最適解が満たす条件などが見出せる可能性も
  2. 実験コードの戦略(2) ・なるべく大きな問題サイズに対して動作するようにする ・仮に O(N!) と O(2^N) の実験方法があった場合、前者では N<=10 程度 までしか見えないが後者なら

    N<=20 まで行ける ・ただし、実装量の差が大きかったり復元が面倒だったりする場合は、 あえて O(N!) のような遅い解法を選択する時も… ・どの程度の問題サイズまでの、どのデータが採れるかを念頭に置いて おき、それで実験として十分かを見極める ・なるべく多様な入力を試す ・1ケースずつ実験するよりは、例えば順列を全通り一気に実験できる ようにする(順列全探索を利用する)方が楽
  3. 実践編(1) 問題 以下の条件を満たす文字列 S は何通りあるか? ・S は 0,1 からなる長さ N

    の文字列 ・S に 0 が 2 つ連続して登場することはない 1 <= N <= 10^{18} これを実験で解くことを考えよう
  4. 実践編(1) 戦略 ・N<=10^{18} と大きい → 何らかの方法で高速に解が求められる! ・思いつく方法 ・とりあえず全探索する ・計算量は O(N

    * 2^N) なので N<=18 までなら情報が得られそう ・該当する文字列はそんなに少なくなさそう? ・ひとまず数表を見てみよう ・01文字列に対しては、 bit 演算による実験が手軽
  5. 実践編(2) 問題 文字列 S が与えられる。 この文字列に以下の操作を K 回加えた結果を出力せよ。 i 回目の操作:S

    のうち [1,i] 文字目の区間を反転する abac → abac → baac → aabc → cbaa 1 <= K <= N <= 10^6 https://www.codechef.com/LTIME103A/problems/RMNTREV
  6. 実践編(2) 戦略 ・制約は N<=10^6 ・O(N) 程度の計算量が要求される ・O(N^2) では厳しいので、何らかの規則性がありそう ・逆に、 O(N^2)

    なら非常に簡単にできる ・N<=3000 あたりまでのデータなら取れる ・この問題は操作の結果を求めるものなので、 N<=10 程度まで見れば 十分そう(もし足りなければ、 N を大きくして実験すればok)
  7. 実践編(2) 戦略(続き) ・この問題は、「文字列」じゃなくてよい ・初期状態が「 (0, 1, 2, …, N-1) の数列」でもよい

    ・操作回数も、 K に固定しなくてよさそう ・操作回数が 1, 2, …, N 回の結果を並べて観察できる N が小さい方から順に実験だ!
  8. 実践編(2) 実験 & 結果 実装例:exp2.cpp 結果:右図 まず、この結果からパターンを見破る パターンは「公差2の数列 + 後ろはそのまま」

    その後、問題を「数列」から「文字列」に戻す すなわち、 i 番目には P_i 文字目を出力する
  9. 実践編(3) 戦略 ・今回も O(N^2) は簡単、適当に回数を決め打って実験すればよさそう ・XORの性質を検討する ・操作後の A_N は、 A_1

    を C_1 回 XOR 、 A_2を C_2 回 XOR … という 形で書けるはず ・さらに、寄与した回数の偶奇のみが重要 ・なので、寄与した回数の偶奇をいい感じに求めることを考えよう
  10. 実践編(3) 実験 & 結果 実装例:exp3.cpp 結果:右図 めちゃくちゃ綺麗なパスカルの三角形が現れた 偶奇 + パスカルの三角形

    → Lucasの定理だ! これで考察が一歩前進する この後は、この寄与が実現するような操作を求めていく (Lucasの定理 → bit演算 → 高速ゼータ変換という流れ 実験のLTの本質から外れるため省略します)
  11. 実践編(4) 問題 高橋さんはいろはちゃんに、ポテトチップスを 1 袋買うおつかいを頼むこと にしました。 ポテチには N 種類の味があり、 i

    番目の味のポテチは A_i 円で、いろはちゃ んはランダムに 1 つ味を選び購入してきます。 高橋さんは、 1,2,3 円玉を 10^{100} 枚ずつ持っています。これらのうち一部 をいろはちゃんに渡し、いろはちゃんがどの味を選んでも丁度支払えるよう にしたいです。 いろはちゃんに渡すべき最小の小銭の枚数を求めてください。 N <= 100, 1 <= A_i <= 10^9 https://codeforces.com/contest/1620/problem/D
  12. 実践編(4) 戦略 ・小銭を選んで X 円支払えるか判定 → 部分和問題 ・部分和問題なのに 1 <=

    A_i <= 10^9 (小銭の枚数も 10^8 オーダー) ・前から少し考察してみる ・最大の金額を Amax とすると、 ceil(Amax/3)+1 枚は達成可能 ・(1,2,3,3,…,3) 円玉を渡せば 1 円以上総和円以下を全部作れる ・また、ceil(Amax/3) 枚未満になることはない ・全部 3 円玉でも足りない
  13. 実践編(4) 戦略(続き) ・ceil(Amax/3) 枚の支払いパターンを考える ・ceil(Amax/3) 枚で Amax 円が払えるパターンを検討 ・もし Amax%3==0

    の場合 (3,3,…,3) ・もし Amax%3==1 の場合 (1,3,…,3), (2,2,3,…,3) ・もし Amax%3==2 の場合 (2,3,…,3) 「このパターン全てについて、どんな金額が払えないか知りたい…」 → ならば実験だ! ・書くコードは普通の部分和問題と同じDPでok
  14. 実践編(4) 実験 & 結果 実装例:exp4.cpp 結果:各ブロックごとに 1,2 行目で部分和問題を与える 3行目に、 0

    以上総和以下で作れない和を出力 これを観察すると、小銭の持たせ方の各パターンについて 作ることのできない金額が見て取れる