AOJ 2741 D - インビジブル
確かに、確かに大事なんですがね?
★つくのか~これ。
感想
問題文を4回くらい読みましょう。分かりつらい。
サンプル2を見てみるとすっごい悩みますが、おそらくこれは最初っからどちらもパスして終わる or どちらも一枚ずつ出したのちパスして終わる感じなのかな?
さて、方針ですが、「プレイヤーは二人ともすべてのカードが見えている(これ大事)」ので、すべての可能性を探索したのち、行動ができます。(できます、じゃないんだよなあ、私は出来ません)
したがって、
「各状態で、今手番のプレイヤーがカードを置いた時と、パスしたときの結果を両方見て、大きいほうを選ぶ。」
というdfsを書きます。n,mは50までと小さいので適当に描いても大丈夫そうです(私は係数の小さいO(n^5)くらい))(適当に書くとバグります)
気を付けるべき個所は
- スタックに何もない状態で二人ともパスするとゲームが終わる。
- -1のカードを置くとそれより下においてある相手のカードは無効になる。
という二点、
dp[type][x1][y1][x2][y2] := 状態がtypeで、プレイヤー1のx2枚目、プレイヤー2のy2枚目からスタックにおいてあり、プレイヤー1がx1枚目、プレイヤー2がy1枚目まで場に出した時から始めた際の答え。(1-index)
でメモ化をしました。
typeに関しては0がプレイヤー1の手番、1がプレイヤー2の手番、2がストックに一枚もない状態でプレイヤー2がパスをしたのちのプレイヤー1の手番、3がストックに一枚もない状態でプレイヤー1がパスしたのちのプレイヤー2の手番です。
答えはdp[0][0][0][0][0]です。
実装力ー
コード
#include <bits/stdc++.h> using namespace std; #ifdef DEBUG_MODE #define DBG(n) n; #else #define DBG(n) ; #endif #define REP(i,n) for(ll (i) = (0);(i) < (n);++i) #define rep(i,s,g) for(ll (i) = (s);(i) < (g);++i) #define rrep(i,s,g) for(ll (i) = (s);i >= (g);--(i)) #define PB push_back #define MP make_pair #define FI first #define SE second #define SHOW1d(v,n) {for(int WWW = 0;WWW < (n);WWW++)cerr << v[WWW] << ' ';cerr << endl << endl;} #define SHOW2d(v,i,j) {for(int aaa = 0;aaa < i;aaa++){for(int bbb = 0;bbb < j;bbb++)cerr << v[aaa][bbb] << ' ';cerr << endl;}cerr << endl;} #define ALL(v) v.begin(),v.end() #define Decimal fixed<<setprecision(20) #define INF 1000000000 #define LLINF 1000000000000000000LL #define MOD 1000000007 typedef long long ll; typedef pair<ll,ll> P; ll dp[4][55][55][55][55]; ll n,m; vector<ll> a; vector<ll> b; ll check(ll hoge,ll x1,ll y1,ll x2,ll y2){ ll ret = 0; for(ll i = x1-1;i >= x2;i--){ if(a[i] != -1)ret += a[i]; } for(ll i = y1-1;i >= y2;i--){ if(b[i] != -1)ret -= b[i]; } return ret; } ll dfs(ll type,ll x1,ll y1,ll x2,ll y2){ if(dp[type][x1][y1][x2][y2] != LLINF)return dp[type][x1][y1][x2][y2]; ll ret; //A if(type % 2 == 0){ ret = -LLINF; //置く if(x1 < n){ if(a[x1] == -1){ ret = max(ret,dfs(1,x1+1,y1,x2,y1)); } else{ ret = max(ret,dfs(1,x1+1,y1,x2,y2)); } } //置かない if(type == 2)ret = max(ret,0LL); else if(x1 == x2 && y1 == y2){ ret = max(ret,dfs(3,x1,y1,x2,y2)); } else{ ll tmp = check(type,x1,y1,x2,y2); ret = max(ret,dfs(1,x1,y1,x1,y1)+tmp); } } //B else{ ret = LLINF; //置く if(y1 < m){ if(b[y1] == -1){ ret = min(ret,dfs(0,x1,y1+1,x1,y2)); } else{ ret = min(ret,dfs(0,x1,y1+1,x2,y2)); } } //置かない if(type == 3)ret = min(ret,0LL); else if(x1 == x2 && y1 == y2){ ret = min(ret,dfs(2,x1,y1,x2,y2)); } else{ ll tmp = check(type,x1,y1,x2,y2); ret = min(ret,dfs(0,x1,y1,x1,y1)+tmp); } } return dp[type][x1][y1][x2][y2] = ret; } int main(){ cin >> n >> m; REP(i,n){ ll tmp;cin >> tmp;a.PB(tmp); } REP(i,m){ ll tmp;cin >> tmp;b.PB(tmp); } REP(i,55)REP(j,55)REP(k,55)REP(l,55){ dp[0][i][j][k][l] = LLINF; dp[2][i][j][k][l] = LLINF; dp[1][i][j][k][l] = LLINF; dp[3][i][j][k][l] = LLINF; } cout << dfs(0,0,0,0,0) << endl; return 0; }
なぜdpの初期化こんな書き方しているの?
添え時に困ったからです(これは罠で、最初初期化の値がtypeによって違っていた(結局取りえない値であればよいことに気がついた))