对应 poj 题目:点击打开链接
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 3066 | Accepted: 1207 |
Description
Input
Output
Sample Input
4 6 2 4 4 5 5 9 3 6 2 5 3 5 7 2 4 4 5 5 9 3 6 2 5 3 5 7 8 6 1 2 2 3 1 3 2 4 2 5 4 5 10 1 2 2 5 3 6 5 8 4 7 6 10 2 4 4 5 4 8 7 8
Sample Output
Game 1: B wins. Game 2: A wins. Game 3: A wins. Game 4: B wins.
题意:
游戏有A、B两人参与。A先走,每人每次任选一条虚线填成实线。而如果某人填完一条线段后,该线段与另外两条相邻的实线组成了一个单位三角形,该三角形被标记为该游戏者所有,且该游戏者必须接着再填一条虚线。当18条线段被填充完毕后,拥有三角形多的玩家获胜。
首先输入一个数 T ,表示试验次数;接着一个数 n,表示当前局面已经走了 n 条边;接着输入 n 对数,每对数字表示一条填充边,由 A 开始。问从给出的局面开始,双方都采取最优策略,最终谁会取胜。
思路:
显然是博弈问题,也是 alpha-beta 搜索算法的基础应用(博弈基础:点击打开链接)。一个比较关键的问题是局面的存储。整个棋盘有18条边,因此可以用一个整型来存储一个局面,具体就是:
如果每条边用红色数字表示(当然也可以不这样排列),则用 2^0 = 1 表示 edge(1, 2) 或 edge(2, 1),用 2^1 = 2(即二进制10)表示 edge(2, 3) 或 edge(3, 2),用2^2 = 4(即二进制100)表示 edge(1, 3) 或 edge(3, 1) ...
那这个局面用哪个数存储?用 2^3 + 2^4 + 2^5 = 56 表示;其余单位三角形可用同样的方法计算。
#include
#include
#include
// 保存每条边代表的数字
int edge[11][11]={
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 3, 5, 0, 0, 0, 0, 0},
{0, 2, 1, 0, 0, 6, 8, 0, 0, 0, 0},
{0, 0, 3, 0, 0, 4, 0, 9, 11,0, 0},
{0, 0, 5, 6, 4, 0, 7, 0, 12,14,0},
{0, 0, 0, 8, 0, 7, 0, 0, 0, 15,17},
{0, 0, 0, 0, 9, 0, 0, 0, 10,0, 0},
{0, 0, 0, 0, 11,12,0, 10,0, 13, 0},
{0, 0, 0, 0, 0, 14,15,0, 13, 0, 16},
{0, 0, 0, 0, 0, 0, 17,0, 0, 16,0},
};
// 保存每个单位三角形代表的数字
int tri[9] = {7, 56, 98, 448, 3584, 6160, 28672, 49280, 229376};
int end_state = (1<<18)-1; // 终结状态 2^18 - 1 ,即所有边均被填充
int inf = (1<<20);
int next_state(int cur_state, int edge, int *cnt)
{
int i;
int new_state = (cur_state | edge); // 当前局面并上一条边形成新局面
for(i = 0; i < 9; i++) // 如果新局面能形成一个新的单位三角形,则 cnt++
if(((cur_state & tri[i]) != tri[i]) && ((new_state & tri[i]) == tri[i]))
(*cnt)++;
return new_state;
}
int alpha_beta(int player, int cur_state, int alpha, int beta, int ca, int cb)
{
int remain;
// 如 A 得到 5 分以上则 A 赢
// 如 B 得到 5 分以上则 A 输
if(ca >= 5) return 1;
if(cb >= 5) return -1;
remain = ((~cur_state) & end_state); // 计算剩余可走的边
if(player){ // A 走
while(remain){ // 有可走边
int move = (remain & (-remain)); // 选择一条可走边
int ta = ca;
int val;
// A 填了边后形成新的局面
int new_state = next_state(cur_state, move, &ta);
if(ta > ca) // 如果 A 得分了,则 A 继续填一条边
val = alpha_beta(player, new_state, alpha, beta, ta, cb);
else // 否则轮到 B 填
val = alpha_beta(player^1, new_state, alpha, beta, ca, cb);
if(val > alpha)
alpha = val;
if(alpha >= beta)
return alpha;
remain -= move; // 把边 move 从剩余可选边 remain 中移除
}
return alpha;
}
else{ // B 走
while(remain){
int move = (remain & (-remain));
int tb = cb;
int val;
int new_state = next_state(cur_state, move, &tb);
if(tb > cb)
val = alpha_beta(player, new_state, alpha, beta, ca, tb);
else
val = alpha_beta(player^1, new_state, alpha, beta, ca, cb);
if(val < beta)
beta = val;
if(alpha >= beta)
return beta;
remain -= move;
}
return beta;
}
}
int main()
{
#if 0
freopen("in.txt","r",stdin);
#endif
int T, w = 0;
scanf("%d", &T);
while(T--){
int i;
int n;
int ans;
int cnt = 0; // 偶数轮到 A 走,奇数轮到 B 走
int cur_state = 0; // 当前局面
int ca = 0; // A 的得分
int cb = 0; // B 的得分
int ta, tb;
int alpha = -inf;
int beta = inf;
scanf("%d", &n);
for(i = 0; i < n; i++){
int u, v;
ta = ca;
tb = cb;
scanf("%d%d", &u, &v);
cur_state = next_state(cur_state, 1< 0)
printf("Game %d: A wins.\n", ++w);
else
printf("Game %d: B wins.\n", ++w);
}
return 0;
}