2013 ACM/ICPC 南京网络赛F题
题意:给出一个4×4的点阵,连接相邻点可以构成一个九宫格,每个小格边长为1。从没有边的点阵开始,两人轮流向点阵中加边,如果加入的边构成了新的边长为1的小正方形,则加边的人得分。构成几个得几分,最终完成九宫格时,谁的分高谁赢。现在给出两人前若干步的操作,问接下来两人都采取最优策略的情况下,谁赢。
分析:博弈搜索,有人说要加记忆化,我没有加也过了……与赤裸裸的博弈搜索的区别在于对于最终状态,并不是谁无路可走谁输,而是谁分低谁输。注意判断分数相等的情况。在搜索中每个节点要么是必胜态,要么是必败态,可参见这里对NP问题的描述:http://www.cnblogs.com/rainydays/archive/2011/05/27/2059781.html
边的存储与判断不好处理,我是使用了edge_row[ ][ ]数组来存储横向边,edge_col[ ][ ]来存储纵向边。并用其左边(横向)或上边(纵向)的点来代表这条边。
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; struct Point { int x, y; Point() {} Point(int x, int y):x(x), y(y) {} }; bool edge_row[5][5]; bool edge_col[5][5]; bool tom_turn; int edge_num; int tom_score, jerry_score; Point get_point(int a) { return Point((a - 1) / 4, (a - 1) % 4); } void add_edge(Point a, Point b, bool value) { if (a.x > b.x || a.y > b.y) swap(a, b); if (a.x == b.x) edge_row[a.x][a.y] = value; else edge_col[a.x][a.y] = value; } int square(Point a) { if (a.x < 0 || a.y < 0 || a.x > 2 || a.y > 2) return 0; bool ret = true; ret = ret && edge_row[a.x][a.y]; ret = ret && edge_col[a.x][a.y]; ret = ret && edge_row[a.x + 1][a.y]; ret = ret && edge_col[a.x][a.y + 1]; if (ret) return 1; return 0; } int get_score(Point a, Point b) { if (a.x > b.x || a.y > b.y) swap(a, b); if (a.x == b.x) return square(Point(a.x - 1, a.y)) + square(a); return square(Point(a.x, a.y - 1)) + square(a); } void input() { tom_turn = true; tom_score = jerry_score = 0; scanf("%d", &edge_num); memset(edge_row, 0, sizeof(edge_row)); memset(edge_col, 0, sizeof(edge_col)); for (int i = 0; i < edge_num; i++) { int a, b; scanf("%d%d", &a, &b); Point p1 = get_point(a); Point p2 = get_point(b); add_edge(p1, p2, true); if (tom_turn) tom_score += get_score(p1, p2); else jerry_score += get_score(p1, p2); tom_turn = !tom_turn; } } bool dfs(int next_score, int previous_score, bool tom_turn) { bool win = false; bool did = false; for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { if (!edge_row[i][j]) { did = true; Point a = Point(i, j); Point b = Point(i, j + 1); add_edge(a, b, true); int score = get_score(a, b); win = !dfs(previous_score, next_score + score, !tom_turn); add_edge(a, b, false); if (win) return true; } if (!edge_col[j][i]) { did = true; Point a = Point(j, i); Point b = Point(j + 1, i); add_edge(a, b, true); int score = get_score(a, b); win = !dfs(previous_score, next_score + score, !tom_turn); add_edge(a, b, false); if (win) return true; } } } if (!did) { if (next_score == previous_score) return !tom_turn; return next_score > previous_score; } return false; } int main() { int case_num; scanf("%d", &case_num); for (int i = 1; i <= case_num; i++) { input(); bool tom_win; if (tom_turn) tom_win = dfs(tom_score, jerry_score, tom_turn); else tom_win = !dfs(jerry_score, tom_score, !tom_turn); printf("Case #%d: ", i); if (tom_win) printf("Tom200\n"); else printf("Jerry404\n"); } return 0; }