CODE THANKS FESTIVAL 2018-G:Sum of cards

詰め切るのが大変。本番で通した人たちすごい。

解法

カードiについて,A_i,B_iどちらか一方のスコアを獲得できるという状況だ。こういう状況は,グラフにしてみたくなる。1からNの数字が頂点,辺をA_i,B_iの間に結んだグラフを考えよう。このグラフは,どの頂点も次数が2であり,円形のグラフの集まりになる。A_i,B_iのどちらを表にするかということを辺の向きで表現しよう。A_i,B_iのうち,辺が入る方を表にする,ということにする。するとこの問題は以下のように書ける。

  • N頂点の,円形のグラフが集まったグラフがある。各頂点には1からNの数字が書いてある。このグラフの各辺に向きをつけ,各辺について,その辺が入る頂点に書いてある数字だけスコアを得る。少なくとも1本は辺が入ってくる頂点がK個以上あるという条件のもとで,スコアの最大値を求めよ。

この問題を解いていこう。

円形のグラフが1つのとき

まずは円形のグラフが1つのときを考えよう。これをどう解くかで既に難しく思えるが,なんとここでdpをする(初見厳しくないか...)。どのような状態を持つべきかを考えよう。まず,便宜的に頂点番号を0,1,n-1とする。nは円形を構成する頂点の数とする。今どの頂点をみているかということは当然必要だろう。また,辺が入ってくる頂点の数に制約があるので,これも状態としてもっておきたい。さらに,この「辺が入ってくる頂点の数」をカウントするために重要な情報がある。それは,頂点i-1頂点iの間の辺の向きである。この頂点の向き次第で,頂点i-1から頂点iに移るときに,辺が入ってくる頂点の数が変わってくる。では

  • dp[i][j][k]:頂点iまでで辺が入る頂点の数がj個,kは頂点i-1と頂点i間の辺の向きを表す。k=0のときに頂点iから頂点i-1に辺が入る。k=1のときはその逆とする。

で十分だろうか?とりあえずこの場合の状態遷移について考えてみよう。便宜上,i=1から始め,i=n-1まで見た後,i=0をみることにする。

k=0のとき

i-1k=0のときは以下の図のようになる。i-1には辺が入っていなかったが,iから出る辺で入ってくるようになるので,jの添え字が1増える。
f:id:sigma1113:20190130234314p:plain
また,i-1k=1のときは以下の図のようになる。i-1にもともと辺が入っていたので,jの添え字は増えない。
f:id:sigma1113:20190130234332p:plain

k=1のとき

同様に考えればよい。
i-1k=0
f:id:sigma1113:20190130234637p:plain
i-1k=1
f:id:sigma1113:20190130234702p:plain

というわけで遷移が書けた。めでたしめでたし...と言いたいのだが,ここには罠がある。そう,いま考えているグラフは円形であったから,頂点0と頂点1の間から順に見ていって,最後に頂点n-1と頂点0の間を見ることになるが,このときに,上で述べた遷移にはならないパターンがある。以下の2つの図を見比べてみよう。
頂点0から頂点1に辺が出ているとき
f:id:sigma1113:20190131000413p:plain
頂点1から頂点0に辺が出ているとき
f:id:sigma1113:20190131000423p:plain
i-1からiに辺を伸ばすときは基本的に辺が入る頂点の数は増えるはずであったが,一周して戻ってくるときだけは,そうならないのである。よって,この3次元DPを素直にやるだけではうまくいかないのだ。ではどうするか?答えはシンプルで,「0から1に辺が出ているか」それとも「1から0に辺が出ているか」ということを状態としてもてばよい。そうすればn-1から0の遷移の際にのみ気をつけることで,この問題を回避できる。つまり

  • dp[i][j][k][l]:頂点iまでで辺が入る頂点の数がj個,kは頂点i-1と頂点i間の辺の向きを表す。k=0のときに頂点iから頂点i-1に辺が入る。k=1のときはその逆とする。lは頂点0と頂点1の間の辺の向きを表す。l=0のときに頂点1から頂点0に辺が入る。l=1のときはその逆とする。

これで円形1つのときの問題を解くことができた。頂点が1つのみのときに注意。

円形のグラフが複数あるとき

さて,この問題でグラフを作ったときに,N個の頂点すべてが連結になるとは限らない。よって,上のようにして各円について問題を解くことはできるが,その結果をまとめてやる必要がある。しかしこれは難しくない。円が全部でM個あるとしよう。各円について,辺が入る頂点がk個あるときのスコアの最大値がわかっている。このとき,floor(\frac{M+1}{2})\leq k \leq Mであることに注意しよう。それならば

  • dp2[i][j][k]:i個目の円のj番目のありえる辺が入る頂点の個数まで考えたときに,合計で辺の入る頂点の個数がkのときのスコアの最大値

j番目のありえる辺が入る頂点の個数」というのは,t=floor(\frac{M+1}{2})とすると,t,t+1,\cdots,Mに新たに1,2,\cdots mという番号を振りなおしたと考えればよい。遷移はナップサック問題のような形になる。このうち,kKより大きいものの最大値が答えとなる。

この解法全体の計算量はO(NK)である。

実装

Submission #4129625 - CODE THANKS FESTIVAL 2018
これが参考になるかわからないが,上で述べたことを書くとこんな感じになる。各円の結果をまとめる部分で,前の円の大きさを保存しておかないといけなかったりなど,実装上面倒なことは多い。もう少し楽な実装方針があるかもしれない。

大変だが,勉強になる問題であった。ちなみにうまくやると最大費用流になるらしい。