kmjp's blog

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

yukicoder : No.1961 Clear Brackets

考察が難しい。
https://yukicoder.me/problems/no/1961

問題

括弧列に対するスコアを、以下のように定める。
括弧列から、正しい括弧列を取り除く、という作業を、これ以上取り除けなくなるまで行う。
作業の最小実行回数がスコアとなる。

今、未確定の文字を含む括弧列Sが与えられる。
未確定の文字をそれぞれ開き括弧・閉じ括弧のいずれかに置き換えられるとき、スコアの最大値を求めよ。

解法

作業を行えなくなった場合、最終的な括弧列は閉じ括弧が0個以上並んだあと開き括弧が0個以上残った形となる。
また、その時のスコアは、先頭・末尾もしくは残る文字の間に挟まれた空でない正しい括弧列の数となる。

A[i]=(S[i]が開き括弧なら1、閉じ括弧なら-1)という数列Aを考え、またAのprefix sumをBとする。
途中までBが小さくなるようにし、途中からBが大きくなるようにすることを考える。
その位置を総当たりしよう。

ただ、Bが単に小さくなるだけではよくない。Bの最小値を更新するような添え字iは、2以上離れている(=間に消せる正しい括弧列がある)のが良い。よって、最小値を更新した直後は、一旦開き括弧を挟むべきである。
つまり、直前の開き括弧の有無を覚えたうえで貪欲に"?"に開き括弧・閉じ括弧を割り当てていこう。

後半のBが大きくなるようにするパートも同様である。

int N;
string S;

int L[202020],R[202020];


void solve() {
	int i,j,k,l,r,x,y; string s;
	
	cin>>N>>S;
	
	int cur=0,sc=0,up=0;
	FOR(i,N) {
		char c=S[i];
		if(c=='?') {
			if(cur||up) {
				c=')';
			}
			else {
				if(i!=N-1&&S[i+1]=='(') {
					c=')';
				}
				else {
					c='(';
				}
			}
		}
		L[i+1]=-1<<20;
		//最小値を維持している間テーブル更新
		if(S[i]!='('&&cur<=1) L[i+1]=sc+up;
		
		if(c=='(') {
			cur++;
			up=1;
		}
		else if(c==')') {
			if(cur) {
				cur--;
				up=1;
			}
			else {
				sc+=up;
				up=0;
			}
		}
	}
	
	cur=0,sc=0,up=0;
	for(i=N-1;i>=0;i--) {
		char c=S[i];
		if(c=='?') {
			if(cur||up) {
				c='(';
			}
			else {
				if(i&&S[i-1]==')') {
					c='(';
				}
				else {
					c=')';
				}
			}
		}
		R[i]=-1<<20;
		//最小値を更新したときだけテーブル更新
		if(S[i]!=')'&&cur==0) R[i]=sc+up;
		
		if(c==')') {
			cur++;
			up=1;
		}
		else if(c=='(') {
			if(cur) {
				cur--;
				up=1;
			}
			else {
				sc+=up;
				up=0;
			}
		}
	}
	
	int ma=0;
	FOR(i,N+1) ma=max(ma,L[i]+R[i]);
	
	cout<<ma<<endl;
}

まとめ

コーナーケースとか地味につらい。