POJ1085 The Triangle War: 记忆化搜索

来源:互联网 发布:2016党章党规网络测试 编辑:程序博客网 时间:2024/06/05 06:30
根本不知道怎么下手,后面看了别人的博客:
http://blog.csdn.net/urecvbnkuhbh_54245df/article/details/5846776
POJ 1085 The Triangle War
 * 题目注意:这道题中对于线段是谁放置的不关心,只要自己放置的线段形成一个完整的三角形就是获得了一次奖励
 * 题目分析:
 *  这道题目是关于博弈的搜索题,这里使用动态规划的思想来做,而动态规划的关键是状态的表示和存储。
 * 由于游戏的局面状态变化总是和线段是否放置相关,于是可以这样来表达和记录局面状态,用二进制形式表示
 * 线段,2^i表示第i条线段(i从0开始),这样的话,局面状态state也可以用线段二进制的或运算结果来表示了,而且
 * 满足:0<= state < 2^18 。
 * 这里还需要注意的是状态表f[state]中记录的是对于当前局面state,某一方先走,然后双方都采取最优策略,最终双方
 * 所拥有的新产生的三角形个数的差值
*/

 

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

还可以用启发式搜索,我还不怎么会,后面再来补充;
他的代码:
题目大意是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<a href="http://lib.csdn.net/base/dotnet" class='replace_word' title=".NET知识库" target='_blank' style='color:#df3434; font-weight:bold;'>.NET</a>/twilightgod/archive/2008/12/12/3505659.aspx
我的代码:(精简一些)
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <queue>#include <map>using namespace std;const int MAXN = 1<<18;//三角形的hash值const int triangle[9][3] = {    {1, 2, 4}, {8, 16, 128}, {4, 16, 32},    {32, 64, 1024}, {256, 512, 32768}, {128, 512, 2048},    {2048, 4096, 65536}, {1024, 4096, 8192}, {8192, 16384, 131072}};//别人的这个数组,学到了,嘿嘿嘿(1, 2, 4, 8 , 10)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}   };   //存线段端点标号 const int line[18][2] = {    {1, 2}, {1, 3}, {2, 3}, {2, 4}, {2, 5}, {3, 5},    {3, 6}, {4, 5}, {4, 7}, {4, 8}, {5, 6}, {5, 8},    {5, 9}, {6, 9}, {6, 10}, {7, 8}, {8, 9}, {9, 10}};//2的幂 const int haha[18] = {    1, 2, 4, 8, 16, 32, 64, 128, 256,    512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072};int dp[MAXN];//状态为i时,放置2的幂为ii的那条线,形成的三角形的个数;int place(int i, int ii) {    int tmp = 0;//三角形个数    for(int k = 0; k < 9; k++) {        int nAngle = 0;        for(int t = 0; t < 3; t++) {            if ((triangle[k][t]&i))                nAngle++;//第k个三角形的第t条边是否已经被放置            if (triangle[k][t]&ii)                nAngle += 2;//这条边在第k个三角形里        }        if (nAngle == 4)//自行体会            tmp++;    }    return tmp;}//用dp坐的话,肯定超时,我还不知道这样写对了没有,多求了很多东西(备忘录搜索的优势,只求了有用的状态,而且写起来简单)/*void init() {    for(int i = 0; i < MAXN-64; i++) {        int m = 0;        for(int j = 0; i < 18; j++)           if (i<<j&1) m++;        if (m > 11) continue;        for(int j = 0; j < 18; j++) {            int ii = 1<<j;            if ((ii|i) == i) continue;            int tmp = place(i, ii);            if (tmp)                tmp += dp[i];            else tmp -= dp[i];            ii |= i;            dp[ii] = max(dp[ii], tmp);        }    }}*///表示在state这个状态下,假设A先走,双方走法都是最优,最终A比B多得到的三角形个数void dfs(int state) {//备忘录搜索    if (dp[state] != -88) {//-88嘿嘿        return ;    }    for(int i = 0; i < 18; i++)        if (!(haha[i]&state)) {//如果第i条线没有放,            int tmp = place(state, haha[i]);//那就放            dfs(state|haha[i]);//下一状态            dp[state] = max(((tmp)?1:-1)*dp[state|haha[i]]+tmp, dp[state]);//自行体会    }}int main() {    int t, m, pp = 1;    for(int i = 0; i < MAXN; i++)        dp[i] = -88;    dp[MAXN-1] = 0;//初始化    scanf("%d", &t);    while (t--) {        scanf("%d", &m);        int x, y, res = 0, now = 1, state = 0;//res,结果,now谁走,state当前状态       //先模拟        while (m--) {            scanf("%d%d", &x, &y);            int tt;            for(int k = 0; k < 18; k++)                if (line[k][0] == x && line[k][1] == y) {                    tt = place(state, haha[k]);                    state |= haha[k];                    break;            }            if (!tt) now *= -1;//换人不是 = ,是 *=            res += now*tt;        }        dfs(state);        res += now*dp[state];        printf("Game %d: %c wins.\n", pp++, res>0?'A':'B');    }}


0 0
原创粉丝点击