説明のために,プレイヤーを区別して左と右と呼ぶことにします.左が先手のときの得られる価値の和の最大化を考えることにします.
を山がそれぞれ , としてゲームを始めたときに,左が得られる価値の和の最大値とします. のときに左と右のどちらが先手なのかを考えるのですが,今回のゲームは交互に行うゲームなので, が偶数ならば左が先手となり, が奇数ならば右が先手となります.
次に, のときの選択肢を考えます.選択肢は, ならば を取るか, ならば を取るかのどちらかになります.よって,左がその局面で先手のときは, が求める値となり,右がその局面で先手のときは, が求める値となります.
上の再帰関数を計算して答え を求めると,計算時間は となります.このままでは TLE となるのですが, の定義を思い出すと は何度計算しても一意に定まります.したがって,メモ化再帰を行うと 時間で計算することができます.
計算時間:
#include <bits/stdc++.h>
using namespace std;
class Game {
public:
Game(int _A, int _B) : A(_A), B(_B), a(_A), b(_B), dp(A + 1, vector<int>(B + 1, -1)) {}
void Input() {
for (auto &x : a) cin >> x;
for (auto &x : b) cin >> x;
}
int Solve(const int i = 0, const int j = 0) {
if (i == A && j == B) return 0;
if (dp[i][j] != -1) return dp[i][j];
int &res = dp[i][j] = 0;
bool snuke = ((i + j) % 2) == 0;
if (i == A)
res = Solve(i, j + 1) + ((snuke) ? b[j] : 0);
else if (j == B)
res = Solve(i + 1, j) + ((snuke) ? a[i] : 0);
else
res = snuke ? max(Solve(i, j + 1) + b[j], Solve(i + 1, j) + a[i])
: min(Solve(i, j + 1), Solve(i + 1, j));
return res;
}
private:
int A, B;
vector<int> a, b;
vector<vector<int>> dp;
};
int main() {
cin.tie(0); ios::sync_with_stdio(false);
int A, B;
cin >> A >> B;
Game g(A, B);
g.Input();
cout << g.Solve() << endl;
return 0;
}
まとめ
2つの山ではなく 個の山としても同様にできますが, 人だとどうなるのでしょうね.たぶん同様にできると思うのですが.