AOJ 2741 Invisible

解法

メモ化再帰で解ける.
memo[si][sj][i][j][turn][pass] := a(si)からa(i-1),b(sj)からb(j-1)までスタックに積まれていて,手番がturnかつパスの状態がpassであるときの最大値.(pass == 1 ならば,スタックが空の状態で一回パスされたことを表す)

先攻は score_a - score_b が最大になるように,後攻は最小になるようにする.

パスしたときに何点入るかだが,よく考えてみると,スタックにある a のカードと b のカードの枚数の差は高々1枚であるので,丁寧に場合分けすると得点が計算できる.(累積和使ったほうがスッキリしたかも?)
あとは普通に遷移を書くだけ.

ソースコード

#include <bits/stdc++.h>
using namespace std;

constexpr int INF = 1e9;

int N, M;
int memo[51][51][51][51][2][2];
vector<int> a, b;

int calc(int si, int sj, int i, int j, int turn) {
    int score_a = 0, score_b = 0;
    if(turn == 0) {
        if(i - si < j - sj) {
            if(b[sj] != -1) {
                score_b = b[sj];
            }
            sj += 1;
        }
        for(int k=0; k<i-si; ++k) {
            if(a[si+k] == -1) {
                score_b = 0;
            } else {
                score_a += a[si+k];
            }
            if(b[sj+k] == -1) {
                score_a = 0;
            } else {
                score_b += b[sj+k];
            }
        }
    } else {
        if(j-sj < i-si) {
            if(a[si] != -1) {
                score_a = a[si];
            }
            si += 1;
        }
        for(int k=0; k<i-si; ++k) {
            if(b[sj+k] == -1) {
                score_a = 0;
            } else {
                score_b += b[sj+k];
            }
            if(a[si+k] == -1) {
                score_b = 0;
            } else {
                score_a += a[si+k];
            }
        }
    }
    return score_a - score_b;
}

// [si, i), [sj, j)
int dp(int si, int sj, int i, int j, int turn, int pass) {
    int& r = memo[si][sj][i][j][turn][pass];
    if(r != INF) {
        return r;
    }
    if(turn == 0) {
        r = -INF;
        if(pass == 1) {
            r = max(r, 0);
        } else {
            r = max(r, dp(i, j, i, j, 1, (i == si && j == sj)) + calc(si, sj, i, j, 0));
        }
        if(i < N) {
            r = max(r, dp(si, sj, i+1, j, 1, 0));
        }
    } else {
        r = INF;
        if(pass == 1) {
            r = min(r, 0);
        } else {
            r = min(r, dp(i, j, i, j, 0, (i == si && j == sj)) + calc(si, sj, i, j, 1));
        }
        if(j < M) {
            r = min(r, dp(si, sj, i, j+1, 0, 0));
        }
    }
    return r;
}

int main() {
    cin >> N >> M;
    a.resize(N);
    b.resize(M);
    for(int i=0; i<N; ++i) {
        cin >> a[i];
    }
    for(int i=0; i<M; ++i) {
        cin >> b[i];
    }
    fill(memo[0][0][0][0][0], memo[50][50][50][50][2], INF);
    cout << dp(0, 0, 0, 0, 0, 0) << endl;
}