POJ1085 The Triangle War: 记忆化搜索

这是一道关于博弈的题目。

 

我从中得到如下的体会:

使用动态规划的方法来解答要比使用极大极小搜索算法,在速度上要快得多。

因为极大极小搜索策略每次都要对当前的局面进行某个最大深度的搜索,从中选取出最佳的走法。这样其实是非常耗时的。

由于这道题目的本身特点,如果使用动态规划的话,根据dp的思想就是保存搜索过程中的所有状态。在dp的搜索过程中,每次都是非常彻底的搜索,而且由于状态已经描述清楚并记录下来,如果产生了相同的状态,就可以直接使用,这样速度是很快的。但是从这道题目可以开出,dp中状态的描述需要进行保持程序设计的简单便捷,另外状态表一定要保持不会发生冲突情况。

 

我的一些理解:

 

/*
 * POJ 1085 The Triangle War
 * 题目注意:这道题中对于线段是谁放置的不关心,只要自己放置的线段形成一个完整的三角形就是获得了一次奖励
 * 题目分析:
 *  这道题目是关于博弈的搜索题,这里使用动态规划的思想来做,而动态规划的关键是状态的表示和存储。
 * 由于游戏的局面状态变化总是和线段是否放置相关,于是可以这样来表达和记录局面状态,用二进制形式表示
 * 线段,2^i表示第i条线段(i从0开始),这样的话,局面状态state也可以用线段二进制的或运算结果来表示了,而且
 * 满足:0<= state < 2^18 。
 * 这里还需要注意的是状态表f[state]中记录的是对于当前局面state,某一方先走,然后双方都采取最优策略,最终双方
 * 所拥有的新产生的三角形个数的差值
*/

 

从这道题目中,可以看出动态规划表存储值的定义需要实现明确的。

 

下面是转载的这道题的dp搜索方法的代码。(感谢: 熊鹏 ID:twilightgod)

 

 

 题目大意是2个人玩一种游戏,每次放1条边,如果生成了三角形,则归他所有 给定一些初始的走法,问都以最优策略走谁最后拿的三角形多? 很早做的,有点难度,详细注释 dp,或者说是博弈 //4448583_AC_485MS_1468K /********************************************************************** * Online Judge : POJ * Problem Title : Triangle War * ID : 1085 * Date : 12/3/2008 * Time : 8:23:54 * Computer Name : EVERLASTING-PC ***********************************************************************/ /* 线段使用二进制存储,2^i表示第i条线段的hash值 0<=state<2^18,二进制表示当前棋盘状态 f(state) 表示在state这个状态下,A先走,双方走法都是最优,最终A比B多得到的三角形个数 0<=i<18,记place(state,2^i)=t,则 f(state)=max { t -f(state|2^i) 当t=0 t +f(state|2^i) 当t>0 } */ #include<iostream> using namespace std; //储存组成每个三角形的线段hash值 int tri[9][3]= { {0x1,0x2,0x4}, {0x4,0x10,0x20}, {0x8,0x10,0x80}, {0x20,0x40,0x100}, {0x200,0x400,0x8000}, {0x80,0x400,0x800}, {0x800,0x1000,0x10000}, {0x100,0x1000,0x2000}, {0x2000,0x4000,0x20000} }; //存线段端点标号 int line[18][2]= { {1, 2},{1, 3},{2, 3},{2, 4},{2, 5},{3, 5}, {3, 6},{4, 5},{5, 6},{4, 7},{4, 8},{5, 8}, {5, 9},{6, 9},{6, 10},{7, 8},{8, 9},{9, 10} }; //2的幂 int exp2[18]= { 0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000,0x8000,0x10000,0x20000 }; /* 全局变量 f[] 将f()的值存下,避免重复计算 state 当前棋盘状态 MIN 初始化的极小值 */ int f[0x40000]; int state; const int MIN=-10; //计算在state这个状态下,放置某线段新得到的三角形数 int place(int state,int hash) { int already,t,get=0; for(int i=0;i<9;++i) { already=0;t=3; for(int j=0;j<3;++j) { //放置的线段是第i个三角形中的第j条 if(tri[i][j]&hash) { t=j; } //第i个三角形中的第j条已经放置 else if(tri[i][j]&state) { already++; } } //不存在于第i个三角形中 if(t==3) { continue; } //则第i个三角形是新得到的 if(already==2) { get++; } } return get; } //f() int dp(int state) { //如果当前状态已算过就直接用 if(f[state]!=MIN) { return f[state]; } int max=MIN,t; for(int i=0;i<18;++i) { //如果第i条线段没有用过 if(!(state&exp2[i])) { //放置第i条线段 t=place(state,exp2[i]); //递归计算 t+=((t)?1:-1)*dp(state|exp2[i]); //更新最大值 max=(t>max)?t:max; } } //存下当前状态的最大值,并返回 return (f[state]=max); } int main() { //freopen("in_1085.txt","r",stdin); //全局初始化 for(int i=0;i<0x40000;++i) { f[i]=MIN; } f[0x3FFFF]=0; /* b 数据块数 n 每块的局数 m 每局已经走的步数 x,y 读入的线段 t 临时变量 player 表示当前谁走(1 A,-1 B) pre 根据给出的步骤A比B多得到的三角形数 */ int b,n,m,x,y,t,player,pre; //cin>>b; //for(int i=0;i<b;++i) { cin>>n; for(int j=0;j<n;++j) { //每局初始化 pre=0; state=0; player=1; cin>>m; for(int k=0;k<m;++k) { //读入线段 cin>>x>>y; for(int l=0;l<18;++l) { //转化为hash并放置 if(line[l][0]==x&&line[l][1]==y) { t=place(state,exp2[l]); state|=exp2[l]; break; } } //新得到三角形 if(t) { pre+=(player*t); } //没有生成新三角形则换另一方走 else { player*=-1; } } //计算状态并输出,>0即A胜 cout<<"Game "<<j+1<<": "<<((pre+player*dp(state)>0)?'A':'B')<<" wins./n"; } cout<<endl; } } 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/twilightgod/archive/2008/12/12/3505659.aspx

 

 

你可能感兴趣的:(游戏,Date,算法,存储)