kmjp's blog

競技プログラミング参加記です

TopCoder SRM 643 Div1 Medium TheKingsArmyDiv1

Resubmitが痛いが、凡ミスで落とさず済んだと考えるか。
http://community.topcoder.com/stat?c=problem_statement&pm=13526

問題

2行×N列のグリッドがある。
初期状態で各セルは'S'か'H'である。
このグリッドに対し、以下の何れかの処理を行える。

  1. あるセルの内容を1つの隣接セルにコピーする。
  2. ある列の内容を左右どちらかの隣列にコピーする。
  3. ある行の連続したセルの内容を、もう一つの行にコピーする。

全セルを'H'にするのに必要な最小処理回数を求めよ。

解法

初期状態ですべて'S'の場合、'H'に出来ない。

実はこの問題1つ目の処理は使う必要はない。
各列の状態別に処理を行う。
列の内容が \begin{array}{l} S\\S \end{array}の場合、隣接した \begin{array}{l} S\\S \end{array}でない隣列の内容をコピーするためにコストを1払う。
(最低1つは'H'を含む列があるので、そこから順々にコピーすれば全ての \begin{array}{l} S\\S \end{array} \begin{array}{lll} S&H&H \\H&S&H \end{array}の何れかに出来る。
また、 \begin{array}{l} H\\H \end{array}はそれ以上何もする必要なないので無視する。

このようにして \begin{array}{l} S\\S \end{array} \begin{array}{l} H\\H \end{array}の列を消すと、 \begin{array}{l} SSSHHSSSHHHHSS\\HHHSSHHHSSSSHH \end{array}のようにHが1個だけの列が並ぶことになる。

ここからは3つ目の処理を行い上下方向のコピーを行う。
3つ目の処理では連続した列をまとめて処理できるので、同じ状態が連続した列は1つと考えてよい。
例えば上記の状態は \begin{array}{l} SHSHS\\HSHSH \end{array}となる。
この状態において、残りの列の数Cが:

  • C==0なら何もしない。
  • Cが奇数なら、Hが少ない方の列にあるHを反対の行にコピーする。たとえば先の状態なら2・4列目を上から下にコピーして \begin{array}{l} SHSHS\\HHHHH \end{array}。後は片方の行が全部Hになるので、全体を反対の行にコピーする。この処理回数は(C+1)/2。
  • Cが2以上の偶数なら、どちらかの行のHを1個ずつ反対の行に移してHだけの行を作り、最後にその行を反対行にコピーすればよいので、処理回数はC/2+1。

このように実はO(N)で解ける。

int N;
class TheKingsArmyDiv1 {
	public:
	int getNumber(vector <string> state) {
		N=state[0].size();
		if(count(state[0].begin(),state[0].end(),'H')+count(state[1].begin(),state[1].end(),'H')==0)
			return -1;
		vector<int> V;
		int i,x,ret=0;
		FOR(i,N) {
			int x=(state[0][i]=='H')*2+(state[1][i]=='H');
			if(x==0) ret++;
			else if(x==3) continue;
			else if(V.empty() || V.back()!=x) V.push_back(x);
		}
		
		if(V.size()%2==1) ret+=(V.size()+1)/2;
		else if(V.size()>0) ret+=V.size()/2+1;
		
		return ret;
	}
}

まとめ

文字で書くと長いけど、コード量は短いね。