POJ1085 The Triangle War: 记忆化搜索

根本不知道怎么下手,后面看了别人的博客:
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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值