第3回ドワンゴからの挑戦状 本戦 A - 計算ドリル

俺のrated500埋めの最後の問題がこれ
atcoderでこういう実装ゲーはかなり珍しい

問題

'0'〜'9'、'+'、'-' のみからなる文字列sが与えられる
これをk文字まで書き換えて得られる、逆ポーランド記法で計算できる正しい文字列のうち、計算結果の最大値を求めよ

1 <= |s| <= 52
0 <= k <= |s|

解法

メモ化しながら区間dp
数式の一番最後の文字は '+' か '-' である必要があり、それ以外の部分は2つの式に分けることができるってのがポイント
この区切りとそれぞれの区間に割り振るkを全探索して更新していく
詳細は↓のコメントの通り

string s;
int n, k, dp[55][55][55][2];
bool fin[55][55][55][2];
const int M = 10000;

// [l,r]をk文字まで変えたときの最(b?大:小)値
int rec(int l, int r, int x, bool b) {
	if (fin[l][r][x][b]) return dp[l][r][x][b];
	fin[l][r][x][b] = 1;
	int &res = dp[l][r][x][b];
	res = (b ? -M : M);
	// どう変えても正しい式にならない場合
	if ((r-l+1)%2==0) return res;
	// 1桁の場合
	if (l==r) {
		if (x) res = (b ? 9 : 0);
		else if (isdigit(s[l])) res = s[l]-'0';
		return res;
	}
	// 一番右の文字は+か-にする
	int xp = x - (s[r]!='+'), xm = x - (s[r]!='-');
	// 2つの式の和の最大値は (最大)+(最大) or (最大)-(最小)
	if (b) {
		reps(i,l,r-1) {
			rep(j,xp+1) {
				int tx = rec(l,i,j,1), ty = rec(i+1,r-1,xp-j,1);
				if (tx!=-M && ty!=-M) chmax(res, tx+ty);
			}
			rep(j,xm+1) {
				int tx = rec(l,i,j,1), ty = rec(i+1,r-1,xm-j,0);
				if (tx!=-M && ty!=M) chmax(res, tx-ty);
			}
		}
	}
	// 最小値は (最小)+(最小) or (最小)-(最大)
	else {
		reps(i,l,r-1) {
			rep(j,xp+1) {
				int tx = rec(l,i,j,0), ty = rec(i+1,r-1,xp-j,0);
				if (tx!=M && ty!=M) chmin(res, tx+ty);
			}
			rep(j,xm+1) {
				int tx = rec(l,i,j,0), ty = rec(i+1,r-1,xm-j,1);
				if (tx!=M && ty!=-M) chmin(res, tx-ty);
			}
		}
	}
	return res;
}

signed main() {
	cin >> k >> s;
	n = s.size();
	int res = rec(0,n-1,k,1);
	if (res==-M) cout << "NG" << endl;
	else cout << "OK" << endl << res << endl;
}

https://beta.atcoder.jp/contests/dwacon2017-honsen/tasks/dwango2017final_a