I: Sweet War
問題
カカオ帝国の女帝アリスとココア公国の王妃ブリアンナは友人で、二人ともチョコレートが大好き。
以下意訳
スタックにチョコレートが幾つか入っている。各チョコレートは個別に栄養価r[i]と幸福度s[i]が決まっている。
AliceとBriannaはターン制のゲームを行う。二人はそれぞれ体力が有る(初期値それぞれA,B)。次の2つの行動から選んで行動する。
Pass 体力が1以上ならば選択できる。体力が1減り、相手のターンになる。
Eat スタックのトップのチョコレートiを食べ、食べた人は体力がr[i]増え、幸福度がs[i]増え、相手のターンになる。
ゲームはスタックが空になると終了。二人は幸福度を最大にするように最適に行動する。ゲーム終了時の二人の幸福度を求めよ。
チョコレートの数N<=150
空腹度の初期値0<=A,B<=10e9
0<=r[i]<=10e9
0<=s[i]
sum s[i]<=150
解法
DPであることが予想できるが体力はDPのキーにできない。幸福度をキーにすることを考える。
二人が連続してパスをすることはゲームとして何も変わらない(と思う)。体力が相手より多い時には先頭のチョコレートを相手に押し付けることができる。
体力の差(A-B)だけを考える。
FA[i][j] := Aliceのターン、スタックのtopがiのとき、残りのゲームでAliceが幸福度j以上を取るために最低限必要な体力の差
FB[i][j] := Briannaのターン、スタックのtopがiのとき、残りのゲームでAliceが幸福度j以上取るために必要最低限の体力の差
DPの式を頑張る。
Aliceはなるべく体力差を小さくしたい。
Briannaはなるべく体力差を大きくしたい。
Passするには体力が相手より大きくなければならない。
FA[i][j],FB[i][j]はjについて単調増加にする。
#include<cstdio> #include<iostream> #include<vector> #include<algorithm> #include<string> #include<cstring> using namespace std; #define REP(i, n) for (int i=0; i<int(n); i++) #define EACH(i,c) for(__typeof((c).begin()) i=(c).begin();i!=(c).end();i++) template<class T> inline T &amin(T &a, const T &b) { if (a>b) a=b; return a; } template<class T> inline T &amax(T &a, const T &b) { if (a<b) a=b; return a; } typedef long long LL; const LL INF = 1LL<<60; int N; LL A, B; LL r[155]; int s[155]; LL FA[155][155]; LL FB[155][155]; int main() { scanf("%d%lld%lld", &N, &A, &B); REP (i, N) scanf("%lld%d", r+i, s+i); int sum = 0; REP (i, N) sum += s[i]; REP (i, 155) REP (j, 155) { FA[i][j] = INF; FB[i][j] = INF; } // no chocolate FA[N][0] = -INF; FB[N][0] = -INF; LL tmp; for (int i=N; i--;) { // Alice's turn for (int j=sum; j>=0; j--) { tmp = INF; amin(tmp, FA[i][j+1]); // monotone if (j >= s[i]) amin(tmp, FB[i+1][j-s[i]]-r[i]); // Eat amin(tmp, max(1LL, FA[i+1][j]+1+r[i])); // Pass FA[i][j] = tmp; } // Brianna's turn for (int j=0; j<=sum; j++) { tmp = -INF; if (j) amax(tmp, FB[i][j-1]); // monotone amax(tmp, FA[i+1][j]+r[i]); // Eat if (j >= s[i]) amax(tmp, min(1LL, FB[i+1][j-s[i]]-r[i]-1)); // Pass FB[i][j] = tmp; } } int ans = 0; REP (i, sum+1) if (FA[0][i] <= A-B) ans = i; printf("%d %d\n", ans, sum-ans); return 0; }