jag2016-domestic.contest.atcoder.jp
問題文
省略
解法(反転して表示)
よくあるゲーム木のmin-max探索,後手は差を最小化,先手は差を最大化するように実装する. 状態数がどれくらいになるのかを見積もるのが本質. 実際山札の残り枚数と,どの区間(※)がスタックに残ってるかみたいなのを考えると,状態として O(NM*(N+M)2)程度しか無い.実際はパスの回数とかどっちのターンかみたいな状態も持つんだけど,それはとても小さい定数倍だからまあ問題ない. 実装を複雑化させないために構造体とmapを用いて実装したので重いけど通る.
(※) 手札の引き方はパスを無視すれば一意で,a_1b_1a_2b_2 ,...というふうな列になるので,この列の区間を考える.
「スタックが空になってから」パスを2回が終了条件な注意しないとWAする.
ソース
#include <bits/stdc++.h> using namespace std; struct State{ vector<int> p1,p2; vector< pair<int,int> > stack; int turn; int passed; }; bool operator < (const State &a,const State &b){ if( a.passed != b.passed ) return a.passed < b.passed; if( a.turn != b.turn ) return a.turn < b.turn; if( a.p1 != b.p1 ) return a.p1 < b.p1; if( a.p2 != b.p2 ) return a.p2 < b.p2; return a.stack < b.stack; } map<State,int> memo; int dfs(const State s){ if( s.passed == 2 ) return 0; if( memo.count(s) ) return memo[s]; int ans; if( s.turn == 0 ){ ans = -1e9; if( s.p1.size() ){ State cpy = s; cpy.stack.push_back({cpy.p1.back(),s.turn}); cpy.p1.pop_back(); cpy.passed = 0; cpy.turn ^= 1; ans = max(dfs(cpy),ans); } { State cpy = s; int profit = 0; int coef1 = 1; int coef2 = 1; for(int i = cpy.stack.size() - 1 ; i >= 0 ; i--){ if( cpy.stack[i].second != s.turn && cpy.stack[i].first == -1 ) coef1 = 0; if( cpy.stack[i].second == s.turn && cpy.stack[i].first == -1 ) coef2 = 0; if( cpy.stack[i].second == s.turn && cpy.stack[i].first != -1 ) profit += coef1 * cpy.stack[i].first; if( cpy.stack[i].second != s.turn && cpy.stack[i].first != -1 ) profit -= coef2 * cpy.stack[i].first; } if( cpy.stack.size() == 0 ) cpy.passed++; cpy.stack.clear(); cpy.turn ^= 1; ans = max(dfs(cpy)+profit,ans); } }else{ ans = +1e9; if( s.p2.size() ){ State cpy = s; cpy.stack.push_back({cpy.p2.back(),s.turn}); cpy.p2.pop_back(); cpy.passed = 0; cpy.turn ^= 1; ans = min(dfs(cpy),ans); } { State cpy = s; int profit = 0; int coef1 = 1; int coef2 = 1; for(int i = cpy.stack.size() - 1 ; i >= 0 ; i--){ if( cpy.stack[i].second != s.turn && cpy.stack[i].first == -1 ) coef1 = 0; if( cpy.stack[i].second == s.turn && cpy.stack[i].first == -1 ) coef2 = 0; if( cpy.stack[i].second == s.turn && cpy.stack[i].first != -1 ) profit += coef1 * cpy.stack[i].first; if( cpy.stack[i].second != s.turn && cpy.stack[i].first != -1 ) profit -= coef2 * cpy.stack[i].first; } if( cpy.stack.size() == 0 ) cpy.passed++; cpy.stack.clear(); cpy.turn ^= 1; ans = min(dfs(cpy)-profit,ans); } } return memo[s] = ans; } int main(){ int n,m; cin >> n >> m; vector<int> a(n), b(m); for(int i = 0 ; i < n ; i++) cin >> a[i]; for(int i = 0 ; i < m ; i++) cin >> b[i]; reverse(a.begin(),a.end()); reverse(b.begin(),b.end()); State s = {a,b,{},0,0}; cout << dfs(s) << endl; }