题意:
游戏有A、B两人参与。A先走,每人每次任选一条虚线填成实线。而如果某人填完一条线段后,该线段与另外两条相邻的实线组成了一个单位三角形,该三角形被标记为该游戏者所有,且该游戏者必须接着再填一条虚线。当18条线段被填充完毕后,拥有三角形多的玩家获胜。
首先输入一个数 T ,表示试验次数;接着一个数 n,表示当前局面已经走了 n 条边;接着输入 n 对数,每对数字表示一条填充边,由 A 开始。问从给出的局面开始,双方都采取最优策略,最终谁会取胜。
【实验原理】
1 最单纯的极大极小算法
局面估价函数:我们给每个局面(state)规定一个估价函数值 f,评价它对于己方的有利程度。胜利的局面的估价函数值为 +oo,而失败的局面的估价函数值为–oo。
Max 局面:假设这个局面轮到己方走,有多种决策可以选择,其中每种决策都导致一种子局面(sub-state)。由于决策权在我们手中,当然是选择估价函数值 f 最大的子局面,因此该局面的估价函数值等于子局面 f 值的最大值,把这样的局面称为 max 局面。
Min 局面:假设这个局面轮到对方走,它也有多种决策可以选择,其中每种决策都导致一种子局面(sub-state)。但由于决策权在对方手中,在最坏的情况下,对方当然是选择估价函数值 f 最小的子局面,因此该局面的估价函数值等于子局面 f 值的最小值,把这样的局面称为 max 局面。
终结局面:胜负已分(假设没有和局).
2.alpha-beta剪枝
简单地说就是用两个参数 alpha 和 beta 表示当前局面的父局面的估价函数值 f()。如果当前局面为 min 局面,则需要借助父局面目前已求得的 f() (即 alpha)来判断是否需要继续搜索下去;如果当前局面为 max 局面,则需要借助父局面目前已求得的 f() (即 beta)。
思路:
显然是博弈问题,也是 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) ...
【实验环境】
VS2012
【实验方案设计】
1. minimax算法
2. alpha-beta剪枝
【源程序】
1. minimax算法
#include<iostream>
#include<cstring>
using namespace std;
int mat[11][11] =
{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0 },
{ 0, 1, 2, 0, 0, 5, 6, 0, 0, 0, 0 },
{ 0, 0, 3, 0, 0, 7, 0, 9, 10, 0, 0 },
{ 0, 0, 4, 5, 7, 0, 8, 0, 11, 12, 0 },
{ 0, 0, 0, 6, 0, 8, 0, 0, 0, 13, 14 },
{ 0, 0, 0, 0, 9, 0, 0, 0, 15, 0, 0 },
{ 0, 0, 0, 0, 10, 11, 0, 15, 0, 16, 0 },
{ 0, 0, 0, 0, 0, 12, 13, 0, 16, 0, 17 },
{ 0, 0, 0, 0, 0, 0, 14, 0, 0, 17, 0 }
};
int tri[9] = { 7, 152, 52, 352, 34304, 3200, 71680, 12544, 155648 };
int STATUS = (1 << 18) - 1;
int bestmax=1<<20;
int bestmin=-1<<20;
int Get_Status(int old, int seg, int &cnt)
{
int now = old | seg;
for (int i = 0; i<9; i++)
{
if ((old & tri[i]) != tri[i] && (now & tri[i]) == tri[i])
cnt++;
}
return now;
}
int minimax(int player,int state,int ca,int cb)
{
if (ca >= 5)
return 1;
if (cb >= 5)
return -1;
if (state == STATUS)
return ca > cb ? 1 : -1;
int remain = (~state) & STATUS;
if(player==1)
{
int ans=-1;
while(remain)
{
int seg = remain & (-remain);
int ta = ca, tb = cb;
int now = Get_Status(state, seg, ta), tmp;
if(ta>ca)
tmp=minimax(player,now,ta,cb);
else
tmp=minimax(player^1,now,ta,cb);
if(tmp>ans)
ans=tmp;
remain-=seg;
}
return ans;
}
else{
int ans=1;
while(remain)
{
int seg = remain & (-remain);
int ta = ca, tb = cb;
int now = Get_Status(state, seg, tb), tmp;
if(tb>cb)
tmp=minimax(player,now,ca,tb);
else
tmp=minimax(player^1,now,ca,tb);
if(tmp<ans)
ans=tmp;
remain-=seg;
}
return ans;
}
}
int main()
{
int t, cas = 0;
cin>>t;
while (t--)
{
int n, u, v;
cin>>n;
int cnt = 0, state = 0;
int ca = 0, cb = 0;
while (n--)
{
cin>>u>>v;
int ta = ca, tb = cb;
state = Get_Status(state, 1 << mat[u][v], (cnt & 1) ? cb : ca);
if (ta == ca && tb == cb)
cnt++;
}
int ans;
if(cnt & 1)
ans=minimax(0,state,ca,cb);
else
ans=minimax(1,state,ca,cb);
char ch=(ans==1)?'A':'B';
cout<<"Game "<<++cas<<": "<<ch<<" wins."<<endl;
}
return 0;
}
2. alpha-beta算法
#include <iostream>
#include <cstdio>
using namespace std;
int mat[11][11] =
{
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 2, 3, 4, 0, 0, 0, 0, 0 },
{ 0, 1, 2, 0, 0, 5, 6, 0, 0, 0, 0 },
{ 0, 0, 3, 0, 0, 7, 0, 9, 10, 0, 0 },
{ 0, 0, 4, 5, 7, 0, 8, 0, 11, 12, 0 },
{ 0, 0, 0, 6, 0, 8, 0, 0, 0, 13, 14 },
{ 0, 0, 0, 0, 9, 0, 0, 0, 15, 0, 0 },
{ 0, 0, 0, 0, 10, 11, 0, 15, 0, 16, 0 },
{ 0, 0, 0, 0, 0, 12, 13, 0, 16, 0, 17 },
{ 0, 0, 0, 0, 0, 0, 14, 0, 0, 17, 0 }
};
int tri[9] = { 7, 152, 52, 352, 34304, 3200, 71680, 12544, 155648 };
int STATUS = (1 << 18) - 1;
int Get_Status(int old, int seg, int &cnt)
{
int now = old | seg;
for (int i = 0; i<9; i++)
{
if ((old & tri[i]) != tri[i] && (now & tri[i]) == tri[i])
cnt++;
}
return now;
}
int MaxSearch(int state, int alpha, int ca, int cb);
int MinSearch(int state, int beta, int ca, int cb)
{
if (ca >= 5)
return 1;
if (cb >= 5)
return -1;
if (state == STATUS)
return ca > cb ? 1 : -1;
int ans = 1;
int remain = (~state) & STATUS;
while (remain)
{
int seg = remain & (-remain);
int ta = ca, tb = cb;
int now = Get_Status(state, seg, tb), tmp;
if (tb > cb)
tmp = MinSearch(now, beta, ca, tb);
else
tmp = MaxSearch(now, ans, ca, tb);
if (tmp < ans)
ans = tmp;
if (tmp <= beta)
return ans;
remain -= seg;
}
return ans;
}
int MaxSearch(int state, int alpha, int ca, int cb)
{
if (ca >= 5)
return 1;
if (cb >= 5)
return -1;
if (state == STATUS)
return ca > cb ? 1 : -1;
int ans = -1;
int remain = (~state) & STATUS;
while (remain)
{
int seg = remain & (-remain);
int ta = ca, tb = cb;
int now = Get_Status(state, seg, ta), tmp;
if (ta > ca)
tmp = MaxSearch(now, alpha, ta, cb);
else
tmp = MinSearch(now, ans, ta, cb);
if (tmp > ans)
ans = tmp;
if (tmp >= alpha)
return ans;
remain -= seg;
}
return ans;
}
int main()
{
int t, cas = 0;
cin>>t;
while (t--)
{
int n, u, v;
cin>>n;
int cnt = 0, state = 0;
int ca = 0, cb = 0;
while (n--)
{
cin>>u>>v;
int ta = ca, tb = cb;
state = Get_Status(state, 1 << mat[u][v], (cnt & 1) ? cb : ca);
if (ta == ca && tb == cb)
cnt++;
}
int ans;
if (cnt & 1)
ans = MinSearch(state, -1, ca, cb);
else
ans = MaxSearch(state, 1, ca, cb);
char ch=(ans==1)?'A':'B';
cout<<"Game "<<++cas<<": "<<ch<<" wins."<<endl;
}
return 0;
}
【实验结果及结论】
结果:
1. minimax算法未剪枝时,能得出结果,但是耗时太大,特别是该检验数据都是5层以上。Oj平台测试如下,显示时间超出(TLE)
2. 加入alpha-beta剪枝后
耗时454ms,这也是poj的边界数据结果。
