当サークル代表のp進大好きbot(以下、筆者)がyukicoderで競技プログラミング作問作業に携わる際に心掛けていることを書いていきます。他の作問者とは気をつけている内容がちょくちょく違うと思いますので、作問作業に携わったことがない人が作問の仕方を学ぶための参考にはあまりならないと思います。
writerとしての心掛け
まず最初に、writer(問題の原案作成者)として作業する際に心掛けていることを説明します。
0:盗用の禁止
学術界隈において、盗用は捏造や改竄と並ぶ重大な不正行為です。研究者がこういった行為を行った場合、研究費の申請資格を失ったり、職を失ったり、国によっては犯罪として処理されるそうです。
当たり前のことですが、著者自身もwriter作業において盗用は絶対に行いません。オマージュをする際も注意深く、瓜田李下の精神で臨みます。具体的には、オマージュ元を問題ページか解説ページに明記します。オマージュと典型の違いに関する話題をたまに目にしますが、特定のオマージュ元のない典型問題であると認識している場合には実際に典型であることが読者にも納得していただけるように類題を解説に貼るようにしていますので、あまり大きな問題は生じないと思っています。
ただし問題を解いた経験の少ない著者では、言語化された典型テクニックそのものをtwitterなどで先に知ってしまうこともあり、実際の出題例を知らなかったり出題例を知っていても典型テクニックとの関係をうまく意識できていなかったりなどすることもございます。その場合は類題を貼れていないということもございますので、類題を教えていただけますようよろしくお願いいたします。
1:creditの明記
学術界隈における常識の1つがcreditの明記です。筆者もwriter作業においてcreditが必要な内容に関しては、問題ページか解説ページのいずれかにcreditを明記することを心掛けております。
例えば出典の明記がその1つです。貢献者の不明な業績などの例外を除き、引用可能な文献を正確に出典として挙げ、本来creditされるべき貢献者に然るべき敬意を払います。
また、名前のついている定理は主張だけ紹介するのではなくきちんと名称を示します。面倒だからと名前を伏せれば、writerは時間が節約できてあわよくばその主張を自分の業績と誤認させることができます。しかしその恩恵はまやかしのもので、本来creditされるべき貢献者に対する不誠実な態度と考えています。
ただし競技プログラミングにおいてはその考えがほとんど普及していないようで、貢献者が不明でない事実であってもある程度有名な事実は貢献者を伏せて解説する傾向があるようです。例えばUnion Findアルゴリズムの計算量評価が典型例で、多くの解説において貢献者が秘匿されていることにお気付きかと思います。
そしてあまり知識が豊富でない筆者がそういった解説を読んだ結果、貢献者が不明でないにも関わらず貢献者不明の古典的知識と誤認してしまい、貢献者をcreditせずに書いてしまうという状況も起こり得ます。そのようなケースではcreditすべき貢献者を教えていただけますようよろしくお願いいたします。
2:解説の対象の意識
どのように解説を書くかは、競技プログラミングで作問を始めてから様々な方々から意見を伺いながら手探りで模索してきたため、昔と今ではスタンスが違ったりします。そして今後も変わっていくかもしれません。とりあえず現状ではどうか、を述べていきます。
現在特に気を付けていることは、解説を読んでもらう対象を意識することです。そして実際に想定している読者は
- その問題の難易度より★0.5個分簡単な問題を主戦場とする競技プログラマー
- プログラミング用語は知っている数学者
の2種類です。この2種類の読者なら努力すれば読めることを目指して、読んでくださる読者に最大限の敬意を払った上で読む時間が無駄だったと感じさせないようになるべく丁寧に解説を書くことを心掛けています。
筆者自身、競技プログラミングにおける非常に丁寧な解説を書く文化に助けられてきて、解説を読んで公開した経験は誇張なく100回に1回くらいしかありません。その恩は解説を読んでくださる読者にお返しするつもりです。
もし想定読者に含まれているにも関わらずどうしても解説が読めないという人がいましたら、それはこちらの落ち度ですので遠慮なくお声掛けください。
3:問題文の明確化
問題文は誰が見ても意図が一意かつ明確になるように書くことを心掛けています。もちろん、誰が見てもという言葉には想定の読者がいるわけで、正確には
- 競技プログラマー
- 数学者
の2種類を想定しています。従って、競技プログラマーにとっては常識であっても競技プログラミングに初めて挑戦する数学者にはその意味を即座に理解することが難しい(仮に意味の類推ができたとしても未定義語が平然と現れるわけはないと信じて定義の読み落としを疑ってしまい問題文を読み返すことになって不必要なタイムロスが生じる)用語はなるべく避けています。これは数学者を特に優遇してるということではなく、競技プログラミングに初めて挑戦する人でも問題文を読んで楽しめることを意識しているものです。極端な例で言うと全ての未就学児にも読めるような問題文にすることは現実的でないので、ある程度共通の言語を知っている対象として身近な「数学者」なら読めるということを目安にしているというわけです。
具体的に避けるべきこととして意識していることは
- 「テストケース」や「カッコ列」などの競技プログラミングの専門用語を断りなく使う。
- 人(頂点、商品、など)が番号付けられたというだけの文脈で(もしくはそのような文脈すらなくても)、断りなく「人1」という呼称を使う。
- 「色$X$と色$Y$」や「音$X$と音$Y$」などと書いた時に、断りなく全ての変数を整数と仮定して$X-Y$や$\max \{X,Y\}$などに言及する。
などです。ただ用語の注意書きが多くなると競技プログラマーにとって不利益が大きいという意見もいただいているため、競技プログラマーなら一意に読解できる用語であってその説明がある程度長いものに関してはなるべく「クリックで開く」機能で秘匿するようにしています。
もちろん、逆に数学者にはある程度断りなく使える用語であっても、競技プログラマーに伝わる可能性がそこまで高くない用語はなるべく説明するようにしています。例えば正整数$B$を法とする際の、分母が$B$と互いに素な有理数の扱いがその典型ですね。他にも、昔は再帰的定義の言葉遣いが上級者にもあまり馴染みのないものだったようで、それに起因してカッコ列の再帰的定義に対して議論が生じていたと耳にしたことがあります。従って数学の文脈では再帰的定義も断りなく使いますが、競技プログラミングの問題文ではある程度の補足を入れるようにしています。
他にも、流儀によって解釈の異なる用語は、そもそも用いないかまたはどの流儀を採用しているかが「自然に」伝わる書き方をするように心掛けています。「自然に」という部分が重要で、例えば
- (多重辺を持ちうる)無向グラフ
と書いた時、これはカッコ書きに関係なくそもそも無向グラフという用語自体が多重辺を持ちうる流儀で解釈する場合は「多重辺を持ちうる」というカッコ書きに意味がなく、持たない流儀で解釈する場合は「多重辺を持ちうる」というカッコ書きをつけようがつけまいが持たないものは持たないのでやはり意味がありません。しかし善意を持って読解してくだされば「多重辺を持ちうる」というカッコ書きは、不明瞭な流儀に追加で与えられた無意味な言明ではなく流儀そのものの明示であることを理解していただけると期待しています。このように読者の善意にある程度委ねることで、より正確な「ここでは無向グラフと言ったら多重辺を持ちうる流儀を採用しているとします」という明記より相対的に短く済ませられ、競技プログラマーの読解の妨げとなることを避けています。
更に、省略記号の使用にも制限を課しています。省略記号は便利なものですが、しばしば曖昧さを生じます。例えば$A_{\ell} + A_{\ell+1} + \cdots + A_r$と書いた時、暗に$\ell + 1 < r$が課されているのか、課されていないなら$\ell = r$の時や$\ell - 1 = r$の時や$\ell -2 = r$の時はどんな値であることが想定されているのか、見た目から明らかであると言うのはやや乱暴なところです。
競技プログラミングにおいては省略記号の解消方法にある程度合意の取れた共通認識が恐らくあるだろうことから省略記号が多用されますが、前述した通り競技プログラミングに始めて挑戦する数学者にも理解できるような問題文を心掛けているため、少なくとも問題文では初出の概念の定義そのものに省略記号を使うことは避け、既に厳密に定義された概念を改めて補足説明するためにだけ省略記号を用いるようにします。解説やサンプルの説明ではその限りではありません。
4:問題文のストーリー性の最小化
問題文にストーリー性をつけるべきか否かという話題をたびたび耳にしますが、この辺はwriterの自己満足にならないように参加者がどう望んでいるかに合わせることが大切だと思っています。従ってそこで以前アンケートを取ったところ、ストーリー性のある問題を好むと回答した人が20票(うち閲覧票8票)のうち0票だったため、ストーリー性は競技プログラミングにおいてさほど望まれていないと考えました。
そこで、問題文にストーリー性を付与する場合には以下の指針を設けております。
- ストーリーをつけることで問題文やサンプルが理解しやすくなる問題(特にグラフやゲーム周りの、純粋に抽象化すると数学用語が難しくなってしまう問題)ではストーリーをつけてもよい。
- ストーリーをつけることで問われているアルゴリズムが明らかでなくなる問題(特にグラフやフローなどの、使用すべき概念に気づくことに難易度がある問題)ではストーリーをつけてもよい。
- ストーリーをつけることが自然である問題(特にナップサック問題や巡回セールスマン問題など、ストーリーが元々付与されている問題)ではストーリーをつけてもよい。
- 上記だけに限ると、ストーリーをつけていること自体がヒントになってしまうので、適宜ダミーのストーリをつけてもよい。
このように、基本的にはストーリーをつけない方針で、なるべくシンプルな問題文を心掛けていきます。
5:入力制約の明確化
入力制約や入力形式の書き方も、解説の書き方同様の事情で昔と今ではスタンスが違い、今後も変わっていくかもしれません。とりあえず現状ではどうか、を述べていきます。
- 問題文から従う制約は、その考察を解いたいのでない限り入力制約にも記載する。
- 数値の単純な範囲ではない非自明な制約であって問題文に書かれていないものは見落としやすいので、入力制約で強調する。
- 数列を半角空白区切りで渡す時などの定型句である「入力は全て整数である」は、数列を入力で与えるという問題文に反するので避ける。
基本的には、なるべく省略せずに全部書くというスタンスですね。
6:入出力形式の可視化
入出力形式は日本語での厳密な説明に加えて、基本的には追加でpreタグを用いて可視化していくようにしています。
問題文と同様、入出力形式の日本語での説明でも省略記号を使うことは避け、日本語での厳密な説明の後のpreタグでの補足説明においてだけ省略記号を用いるようにします。
そのため、preタグを日本語での説明の前に書くか後に書くかが問題によって変わったりします。
7:サンプルの充実
サンプルはマルチテストケースでない限り、なるべく最低3個は用意することにしています。ただし3個以上の例を実験することが困難な問題など、サンプル自体が非常に重要なヒントとなってしまいうる場合にはその限りではありません。
サンプルは入出力方法の理解に繋げることを主たる目的として作成し、サンプルがないと問題文の理解が困難であるような状況では問題文の書き直しを検討します。すなわち、問題文の読解の一意性はサンプルを含めずに担保するものとします。
8:作問チェックリストの遵守
作問チェックリストに書かれている内容は当然従うようにしますが、例外として以下の項目に気をつけます。
- テストケースの個数は原則20個以上50個以下とする。ただしマルチテストケース問題では20個を下限とする必要性が薄いので適宜減らし、またジャッジ負荷の重い問題は同一コンテストで出題する予定の他の問題次第でなるべく少なくなるように気をつける。一方で嘘解法の通りやすい問題は50個を上限としないようにする。
- 難易度はヘルプとyukicoderの過去1年分の過去問両方を基準に設定する。特に★2以下の問題はヘルプの難易度基準を参考に勉強しながらupsolveする人も多いだろうことと、実際にこのレベル帯はヘルプの難易度基準から大きく外れる過去問が少ないことから、ヘルプの基準を逸脱しないように気をつける。逆に★2以下の問題でヘルプの難易度基準を大幅に逸脱している問題は、過去問の中でも外れ値とみなし参考にしない。
9:testerさんへの感謝
当たり前のことですが、testerさん(writerさんのご作品に不備がないかなどを確かめる補助者)への感謝は明確に言葉で表します。まだまだ競技プログラミングの作問には不慣れな筆者ですが、それでもコンテストを開かせていただけるのはtesterさんの多大な恩恵あってこそですし、またtesterさんのフィードバックによって様々なことも学べます。貴重なお時間をくださったtesterさんへの感謝を忘れず、もしいつかtesterさんがお困りの時は積極的にお力になれるよう心掛けています。
またtesterさんによっては忙しい中に時間の合間を縫って何ヶ月もお時間を割いてくださることもあります。そういう場合には途中経過を思い出しやすくするように、数週間から1ヶ月に一度くらいはリマインダをご送付するように配慮しています。
10:参加者への感謝
こちらもやはり当たり前のことですが、問題に取り組んでくださる方々への感謝の気持ちも忘れません。そのためにも、なるべく多くの参加者に満足していただけることを目指して作問しています。まだまだ競技プログラミングの作問に関しては未熟な身なので、問題がつまらないとご批判を受けたり、学びのない謎の問題とご批判を受けたりと、空回りを感じてしまい悲しい思いをすることも少なくありません。そういう意見をいただくたびに、それほど低質な評価を受けているコンテストでもなお参加してくださっている方々には最大限の恩返しをしなければならいないと思い、よりいっそう改善点を模索しています。
具体的には、色々な方々に直接ご意見を伺わせていただいております。皆さん温かく忌憚ないご意見をくださいますので、いつも本当に参考にさせていただいております。これからも、なるべく参加者が楽しめるように、そして何を学べるかを明確にできるように、問題と解説を練り上げていくことを心掛けていきます。
testerとしての心掛け
次に、testerとして作業する際に心掛けていることを説明します。
11:補助者としての責任の意識
単なる補助者だからといって責任を放棄するのではなく、不備を見逃した場合は自分の責任でもあることを意識して問題の精査を行います。
問題が一意に読解できるか否かを報告し、実際に2・3個の言語で解いてみて実行時間制限の適切さを確認し、テストケースが入力制約や入力形式と整合的であることや十分強いかをassertなどで調べ、解説が妥当であるかを検証し、作問チェックリストを再確認し、できる限り既出の検出を行います。
とはいえ世界中のすべての公開問題を確認し解法も理解していることは現実的に不可能であるので、作問者たちの責任にも限度があります。そこで自分にできる範囲の1つの目安として★3以下であればyukicoderの同一難易度の過去問1年分は確認するように心掛けています。
12:補助者としての節度の意識
問題の共同制作者としてtesterとして責任を持つ一方で、問題はあくまでwriterさんの作品であることを忘れません。意見の押し付けを行うことは避け、writerさんがwriterさんご自身の望む完成形に近付けるためのお手伝いをするという意識でフィードバックします。
これはあまり難しいことではありません。自分のwriterとしてのスタンスは上述した通りあまり標準的でないということが分かっているので、自分のスタンスとのずれがあってもそれが当然だと理解するため、あくまでフィードバックを送るだけに留めます。
13:能力の限度の提示
現状だと★2.5までは特に問題なくこなせていると考えています(もしこれが自惚れだったらすみません……)。1問当たり数時間から2日程度でこなす予定で、基本的にはコンテスト時間オーバーとなるくらい時間がかかっても諦めず自力で解くようにしています。もしそれでも問題が自力で解けない場合は何日も音沙汰なく悩むのではなくきちんと報告し、解説ACとすべきか否かを相談するようにしています。
一応★3以上を引き受けることもございます。この際は、解説ACになる可能性が高い旨を予めご相談した上でお引き受けしています。特に★3.5以上の場合は、yukicoderの過去問を1年分全ては解いてはいないため難易度評価をする能力に乏しいことも明確にします。
ちなみに普段は★2.5までのtester募集を貪欲に応募するようにしています。たまにtester作業の練習のために★3も応募している感じで、writerさんから直接のご依頼があった際には★3以上でも上記の注意事項(解説ACなど)をご納得していただいた上で基本的には全部お引き受けしています。難しい問題をご依頼いただけるのは、testerとして信頼されているようで嬉しいですね。
長くなりましたが、以上です。お読みいただきありがとうございます。