ツバサの備忘録

主に備忘録代わりに精進記録を載せていくつもりです。

九州大学プログラミングコンテスト2018 F - Team Making

問題
提出コード

解法

制約にbitDPをしてくださいと書いてあります。
ので、実際にbitDPを考えてみます。

  • dp[S] = 今チームを組み終わっている人の状態がSであるときの、これ以降のチームの組み方

とすると、まだチームを組めていない人の中で組めるようなチームの集合をMとして、
\displaystyle dp[S] = \sum_{m \in M} dp[S+m]
という遷移をしたくなりますが、これでは重複部分が存在してしまいます。
例えば、N = 2, K = 3000, A_{1} = A_{2}  = 1000
のようなパターンだと、上の遷移を行うことで、

  • 先に1人目だけの1人のチームを組んでから、2人目だけのチームを作成

  • 先に2人目だけの1人のチームを組んでから、1人目だけのチームを作成

というような、選ぶ順序を逆にしただけのものを区別することになります。
これを避けるために、まだ選んでいない人のうち、最も番号が若い人を確実に選ぶ、という制約を加えてあげると、うまく重複なく数え上げることができます。上の具体例では、前者だけがピックアップされます。
ということで、
Mを、改めて

  • Sに含まれていない人の中で、最も番号が若い人を必ず含むようなチームの組み方の集合

と定義しなおすことで、
\displaystyle dp[S] = \sum_{m \in M} dp[S+m]
という遷移でこの問題を解くことができるようになります。
1人チームは、その人のレートがKを超えていようがいまいがチームを組めることに注意してください。
初期化はdp[S_{all}] = 1で(S_{all}は全ての人を含んだ状態)、答えはdp[\varepsilon] (\varepsilon空集合、すなわち誰も含まれていない状態)を見ればよいです。

感想

左側を固定する方法、どこかで見たような気がするのですが思いつきませんでした...
個人的にやっぱりbitDPは再帰の方が理解しやすいです。