ひとりで勝手にはじめた蟻本読書会 しゃくとり法 最大区間・最小区間 数え上げ 蟻本読書会 の続きです。

ライツアウトは、5×5の形に並んだライトをある法則にしたがってすべて消灯 (lights out) させることを目的としたパズルです。これに似た問題が競技プログラミングではよく出題されます。

選択した場所とその周辺の電球のONとOFFが入れ替わる問題の場合、同じ場所を 2 回選択したら元の状態に戻る、操作の順序を入れ替えたとしても結果は変わらないという特徴があります。これは同じ場所を 2 回選択することはありえない、選択回数としてありうるのは 1 か 0 かの 2 つだけです。

電球が直線上に並んでいる場合は端から考える貪欲法で解を求めることができます。

端から順に決まる貪欲法

C – Boxes and Candies

C – Boxes and Candies

N 個の箱が横一列に並んでいます。 最初、左から i 番目の箱には a i 個のキャンディが入っています。

キャンディが 1 個以上入っている箱をひとつ選び、その箱のキャンディを 1 個食べることで、どの隣り合う 2 つの箱を見ても、それらの箱に入っているキャンディの個数の総和が x 以下にしたいと考えています。

そのために必要な操作回数の最小値を求めてください。

入力されるデータ
N x
a 1 a 2 … a N

(ただし、2 ≦ N ≦ 10^5
0 ≦ a_i ≦ 10^9
0 ≦ x ≦ 10^9 )

i を 順番に見ていって、a[i] + a[i+1] > x なら a[i] + a[i+1] = x となるまで a[i] と a[i+1] を減らさなければなりませんが、このとき a[i+1] から先に減らすのが最適です(i がインクリメントされたときに a[i+1] が再度 a[i] として評価対象になるため)。

差分更新で計算量を減らす

Face The Right Way (POJ No.3276)

Face The Right Way (POJ No.3276)

「N頭の牛が並んでおり、それぞれ前/後どちらかを向いている。連続したK頭の方向転換ができる機械を何回か用いて、全頭の向きを前に揃えたい。最小の機械の使用回数と、そのときの最小のKを求めよ。」

前問では区間の長さは 2 で固定でしたが、今回はその区間の長さも考えよという問題です。

i番目の牛が後ろ向きの場合、「i番目の牛を先頭とする連続するK頭の牛の向きを変える」という操作を i=0 のときから順に繰り返し、これができるときの K の最大値を求めればよいのですが、これだと計算量がO(N^3)になり制限時間以内には終わりません。

そこで牛の区間[i, i+K-1]にて反転したかどうかを配列 isTurn[i] として定義します。牛 i が反転しているかどうかは、区間[i-K+1, i-1]でisTurn[i]の総和を取り、これが奇数なら最初の向きに対して反転していると判断できます。

2回やったら元に戻る性質を利用する

H – 植林

H – 植林

林は H × W 個のセルを持つ長方形領域とみなすことができる。各セルは木が 1 本生えているか 1 本も生えていないかのどちらかである。

セル (i, j) に使い魔を召喚すると i 行目か或いは j 列目にある H + W – 1 個のセルに対し、木が生えていなければ木が植えられ、木が生えていれば木が消されるという操作が行われる。

召喚には多くの魔力を必要とするので、できるだけ少ない召喚回数で林を木で覆いつくしたい。どのセルに使い魔を召喚すれば最小の召喚回数で林を木で覆いつくすことができるだろうか?

入力されるデータ
H W
a[1, 1] a[1, 2] … a[1, W]
a[2, 1] a[2, 2] … a[2, W]

a[H, 1] a[H, 2] … a[H, W]

(ただし、H,W は偶数。
2 ≦ H,W ≦ 1000
a[i, j] は 0 または 1 )

出力するデータ
b[1, 1] b[1, 2] … b[1, W]
b[2, 1] b[2, 2] … b[2, W]

b[H, 1] b[H, 2] … b[H, W]
(ただし b[i, j] は 0 または 1。召喚する場合は 1、しない場合は 0 )

H と W が偶数という制約により、i 行すべてと j 列すべてに召喚するとセル(i, j)の状態だけを反転させることができます。セル(i, j)以外の i 行のセルは W 回、j 列のセルは H 回(W と H はともに偶数)反転するので変化はしません。また i 行にはなく j 列にもないセルはちょうど 2 回反転するのでこれも変化はしません。セル(i, j)だけが奇数回反転するので状態が変化するのです。

そこで セル(i, j) に木がない場合は i 行すべてと j 列すべてを反転させます。偶数回反転させると元に戻るので、各セルを反転させた回数を記録し、奇数回反転させたセルだけ 1 を出力し、そうでないセルは 0 を出力します。

反転 (ライツアウト)

Fliptile (POJ No.3279)

Fliptile (POJ No.3279)

M × N グリッド (1 ≦ M ≦ 15; 1 ≦ N ≦ 15) があり、そこには片面が黒、もう片面が白の正方形タイルが置かれています。

特定のタイルを選択するとそれに隣接するタイルも同時に裏返ります。

すべて裏にするために必要な反転回数の最小値と、その最小値を達成するために反転する場所を出力してください。複数ある場合は文字列としてみたときに辞書順最小のものを、タスクが不可能な場合は「IMPOSSIBLE」を出力してください。

入力されるデータ
M N
A[1, 1] A[1, 2] … A[1, N]
A[2, 1] A[2, 2] … A[2, N]

A[M, 1] A[M, 2] … A[M, N]

(ただし、1 ≦ M ≦ 15, 1 ≦ N ≦ 15
A[i, j] は 0 または 1 )

貪欲法で端から順に決めることができればよいのですが、この場合はそうはなっていません。しかし 1 行目全体が決定すると 2 行目以下は貪欲法で適切な動作が決まります。1 行目の動作は辞書順で全通り試します。