ARC 038 B
今回はこの問題。
h * wのマスがあります。このマス上には障害物があることがあり、「#」で表されます。
(1, 1)のマスに駒をおきます。駒が(i, j)にあるとき、プレイヤーは交互にこのマスを(i + 1, j), (i, j + 1), (i + 1, j + 1)のいずれかに動かすことができます。障害物のあるマスには動かせません。また、範囲外には動かせません。自分のターンの際に駒を動かせない時、その人の負けになります。
h, wの値と、マスの状態が与えられます。プレイヤーが二人とも最善を尽くした場合、どちらが勝つか判断しなさい、という問題です。
回答はこちら。
#include <cstdio> #include <iostream> #include <cmath> #include <cstring> #include <sstream> #include <algorithm> #include <cstdlib> #include <map> #include <queue> #include <utility> #include <vector> #include <set> #include <memory.h> #include <iomanip> #include <bitset> #include <list> #include <stack> using namespace std; #define mod 1000000007 int h, w; char s[101][101]; int result[101][101][2] = {}; int solve(int x, int y, int player) { if(result[x][y][player] != 0) return result[x][y][player]; int dx[3] = {0, 1, 1}; int dy[3] = {1, 0, 1}; int ans; if(player == 0) ans = -1; else ans = 1; for(int i = 0; i < 3; i++){ int nx = x + dx[i], ny = y + dy[i]; if(nx > h || ny > w || s[nx][ny] == '#') continue; if(player == 0) ans = max(ans, solve(nx, ny, 1)); else ans = min(ans, solve(nx, ny, 0)); } result[x][y][player] = ans; return ans; } int main() { cin >> h >> w; for(int i = 1; i <= h; i++){ for(int j = 1; j <= w; j++){ cin >> s[i][j]; } } if(solve(1, 1, 0) == 1) cout << "First" << endl; else cout << "Second" << endl; return 0; }
ゲーム木とよばれる手法(?)を使ってときます。現状から遷移できうる状態を全探索して、最善手をとる、という感じの方法です。
同じところを何度も探索するのは無駄なので、記録していくことにしました。result[i][j][k]には、(i, j)のマスに駒があり、k = 0のときはプレイヤー1、k = 1のときはプレイヤー2のターンである際に、この後最善手を取るとどちらが勝つか、を記録しています。この値が1ならばプレイヤー1が、-1ならばプレイヤー2が勝ちます。
solve関数のansについて、player = 0のときは初期値を-1にしていますが、まず最悪の場合である相手が勝つ、という予測?設定?をしておいて、maxを用いて書き換えると楽な気がします。これ以上進めない、つまりプレイヤー1が負けるときは、ansの値が変更されないということになるので、そのまま-1を返すことができて都合がいいです。プレイヤー2の場合はこれを逆にしただけです。