mini notes

競技プログラミングの解法メモを残していきます。

DPまとめコンテスト L - Deque

L - Deque

概要

数列a_{N}が与えられ、二人のプレイヤーが交互に数列から数を取り除いてゆき、取り除いた数の得点を得るゲームを行う。
先手の得点をX、後手の得点をYとするとき、先手はX - Yを最大化、後手はX - Yを最小化しようとする。
二人のプレイヤーが最適に行動したときのX - Yを求めよ。

制約

1 \leq N \leq 3000
1 \leq a_{i} \leq 10^9

方針

dp[i][j] :
 a_{i+1}からa_{j}でゲームをするときのX-Yの最大値
 ただし、手番は「元のゲームを行ってその局面になった時の手番」とする
 (右半区間でゲームの得点をメモする)

DPの推移は次の通り。

  • dp[i][i+1] = \pm a_{i}、ただし符号は実際のゲームで残り1枚の時の手番が先手ならプラス、後手ならマイナス
  • [左端, 右端) = [l, r)での得点を求めたい場合、「[l+1, r)からlをとるケース」と、「[l, r-1)からr-1をとるケース」の最適な方を選べばよい

解答

Submission #3974769 - Educational DP Contest / DP まとめコンテスト

#include <bits/stdc++.h>
 
#define FOR(i,a,b) for(int i=(a);i<(b);i++)
#define rep(i,n) FOR(i,0,n)
#define RFOR(i,a,b) for(int i=(a)-1;i>=(b);i--)
#define rrep(i,n) RFOR(i,n,0)
 
using namespace std;
 
typedef long long ll;
typedef unsigned long long ull;
 
int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
 
	int n;
	cin >> n;
 
	ll a[n];
	rep(i, n) cin >> a[i];
 
	ll dp[n+1][n+1];
	rep(i, n+1) rep(j, n+1) dp[i][j] = 0;
	// dp[i][j] :
	//  ai+1からajでゲームをするときのX-Yの最大値
	//  ただし、手番は「元のゲームを行ってその局面になった時の手番」とする
 
	for(int len = 1; len <= n; len++){
		for(int l = 0; l < n - len + 1; l++){
			int r = l + len;
			if((n - len) % 2 == 0){
				dp[l][r] = max(dp[l+1][r] + a[l], dp[l][r-1] + a[r-1]);
			}else{
				dp[l][r] = min(dp[l+1][r] - a[l], dp[l][r-1] - a[r-1]);
			}
		}
	}
 
	cout << dp[0][n] << endl;
}