UVa12180/LA4300 The Game
UVa12180/LA4300 The Game
- 题目链接
- 题意
- 输入格式
- 输出格式
- 分析
- AC 代码
题目链接
本题是2008年icpc欧洲区域赛西南欧赛区的E题
题意
这是一个双人游戏。两个人面对面坐下,每个人正前方有m(1≤m≤4)个杯子,右手边是一只大碗。不难发现,一共有2(m+1)个杯子和碗,它们摆成一个圈。游戏开始时,这些杯子和碗里有一些石子,加起来不超过15 个。每个游戏者碗里的石子数代表他的得分。
游戏规则如下:双方轮流操作,每次游戏者可以选择一个他自己的有石子的杯子,按照逆时针顺序把这些石子放到该杯子的各个相邻杯子/碗中(包含该杯子自己),每次放一个石子。
如下图左所示的局面中,如果下方的游戏者选择杯子A,则需要把杯子A 里的4个石子分到杯子B, C,自己的碗和杯子 a 里。这样比分从3:4 变成了4:4。
有 3 种特殊情况。
第一,如果最后一个石子放在了自己碗里,需要再操作一次。如果最后一个石子又放在了自己的碗里,还需要再操作一次,次数不限,直到不满足这个条件为止。
第二,如果最后一个石子放到了对方的杯子里,可以把这个杯子和自己对应的杯子交换(也可以不交换,A 和 a 对应,B 和 b 对应…)。如果自己对应的杯子为空,则不能交换。双方在整个游戏中各有三次交换机会,而且当一个杯子被交换过以后,接下来的4 轮中不能再次被交换(一次连续操作称为一轮)。考虑如下的操作序列:P0, O1, O2, O3, P4, O5, P6, O7,其中Pi是己方的操作,Oj 是对方的操作。如果P0 中交换了某两个杯子,接下来的四轮(第一轮是O1, O2, O3,第二轮是P4,第三轮是O5,第四轮是P6)都不能再交换这两个杯子,但O7 中可以再次交换。
第三,如果最后一个石子放在己方的杯子里,并且放之前这个杯子是空的,并且这个杯子正对的杯子(上图中A 正对着c,B 正对b,C 正对a)在放石子之后非空,这两个杯子中的所有石头都将移到自己的碗里。注意,这种情况不会引起连锁反应(即接下来轮到对方操作)。
所有杯子都为空时游戏结束。如果轮到某一方操作时他的所有杯子均为空,则下一轮仍该对方操作(但对于交换规则来说,“无法操作”也将算作一轮);但只要有非空杯子,游戏者必须操作,而不能跳过这一轮。
假设双方都是足够聪明的,己方得分与对手的得分之差最大是多少?(己方得分大时,结果越大越好,己方得分小时,结果越小越好)。
输入格式
第一行为一个整数 T(0 < T < 60)代表测试数据组数。接下来每组测试数据包含两行,第一行是正整数 M (0 < M < 5) 代表每一方的杯子数,第二行前 M 个整数代表逆时针顺序己方杯子内各自的石头数量,第 M+1 个整数代表己方碗里的石头数量,接下来 M+1 个整数以同样的形式描述对方的杯子和碗里的石头数量。
输出格式
对于每组测试数据,输出一行,输出游戏结束时己方得分与对手的得分之差最大是多少。初始时对方先操作。
分析
典型的极大极小搜索,上 alpha-beta 剪枝即可。主要难点在于对题意的仔细理解,笔者的翻译已经很详细了,参照翻译去做比看原英文题面省事。
AC 代码
#include <iostream>
using namespace std;struct state {int g[10], c[4], s[2], t; bool f;} s; int m, r, t, x;int alphabeta(const state &e, int alpha, int beta) {if (e.g[m] + e.g[r-1] == x) return e.g[m] - e.g[r-1];int k = e.t & 1 ? m+1 : 0; bool g = false;for (int i=0; i<m; ++i) if (e.g[k+i]) {state f = e; int p = (k+i + e.g[k+i]) % r; f.g[k+i] = 0; g = true;for (int j=0, c=e.g[k+i], x=k+i+1; j<c; ++j) ++ f.g[(x+j) % r];if (!(f.f = p == k+m)) {int q = m+1 - k, s = k + p-q; ++ f.t;if (p >= k && p < k+m && e.g[p] == 0 && f.g[t-p]) f.g[k+m] += f.g[p] + f.g[t-p], f.g[p] = f.g[t-p] = 0;else if (p >= q && p < q+m && f.g[s] && f.s[e.t & 1] < 3 && e.t - f.c[min(p, s)] > 4) {++ f.s[e.t & 1]; swap(f.g[p], f.g[s]); f.c[min(p, s)] = e.t;e.t & 1 ? beta = min(beta, alphabeta(f, alpha, beta)) : alpha = max(alpha, alphabeta(f, alpha, beta));if (beta <= alpha) return e.t & 1 ? beta : alpha;-- f.s[e.t & 1]; swap(f.g[p], f.g[s]); f.c[min(p, s)] = e.c[min(p, s)];}}e.t & 1 ? beta = min(beta, alphabeta(f, alpha, beta)) : alpha = max(alpha, alphabeta(f, alpha, beta));if (beta <= alpha) return e.t & 1 ? beta : alpha;}if (!g) {state f = e; f.f = false; ++f.t;e.t & 1 ? beta = min(beta, alphabeta(f, alpha, beta)) : alpha = max(alpha, alphabeta(f, alpha, beta));}return e.t & 1 ? beta : alpha;
}void solve() {cin >> m; r = m+1 << 1; t = m<<1; x = 0;for (int i=0; i<r; ++i) cin >> s.g[i], x += s.g[i];cout << alphabeta(s, -15, 15) << endl;
}int main() {int t; cin >> t; s.c[0] = s.c[1] = s.c[2] = s.c[3] = -4; s.s[0] = s.s[1] = 0; s.t = 1; s.f = false;while (t--) solve();return 0;
}