zoukankan      html  css  js  c++  java
  • bzoj 3106

    好久没写oi系列的题解了

    要不是为了大作业我才不会回来学这些奇怪的东西呢

    本题对抗搜索就好啦

    首先要分析一点,就是由于我们的黑棋每次走两步,白棋只走一步而且是白棋先走,所以除非白棋第一步吃掉黑棋,否则黑棋必胜

    接下来就是计算黑棋如何取胜的问题了

    首先简单介绍一下对抗搜索

    我们知道,两个人下棋,两个人都想赢(或者至少不想输得那么惨),那么这个问题可以转化成——第一个人想赢,而第二个人想让第一个人输(或者赢得不容易)

    这就是对抗搜索得思想:如果我们对每个局面给出一个估值,估值越大表示第一个人越优,那么在第一个人下棋的时候,他的目的是要使当前局面的估值最大

    而对第二个人而言,他的目的是使当前局面的估值最小

    于是我们利用dfs+记忆化来实现就可以了

    注意这里反一下,黑棋要使自己的步数最小

    这里记忆化要记录步数,其原因是有可能这两个人出现来回交换位置转圈的情况,这种情况要通过步数来区分

     1 #include <cstdio>
     2 #include <cmath>
     3 #include <cstring>
     4 #include <cstdlib>
     5 #include <iostream>
     6 #include <algorithm>
     7 #include <queue>
     8 #include <stack>
     9 using namespace std;
    10 const int inf=1e9;
    11 int x1,x2,yy,y2;
    12 int n;
    13 int to[9][2]={{0,0},{1,0},{0,1},{-1,0},{0,-1},{2,0},{0,2},{-2,0},{0,-2}};
    14 int ret[21][21][21][21][61][2];
    15 bool check(int x,int y)
    16 {
    17     return x>0&&x<=n&&y>0&&y<=n;
    18 }
    19 int dfs(int xw,int yw,int xb,int yb,int typ,int dep)//typ=1表示黑棋走,typ=0表示白棋走
    20 {
    21     if(dep>3*n)return inf;
    22     if(ret[xw][yw][xb][yb][dep][typ])return ret[xw][yw][xb][yb][dep][typ];
    23     if(xw==xb&&yw==yb)
    24     {
    25         if(typ)return inf;
    26         else return 0;
    27     }
    28     int temp=typ?inf:0;
    29     if(typ)
    30     {
    31         for(int i=1;i<=8;i++)
    32         {
    33             int tx=xb+to[i][0],ty=yb+to[i][1];
    34             if(check(tx,ty))temp=min(temp,dfs(xw,yw,tx,ty,typ^1,dep+1));
    35         }
    36     }else
    37     {
    38         for(int i=1;i<=4;i++)
    39         {
    40             int tx=xw+to[i][0],ty=yw+to[i][1];
    41             if(check(tx,ty))temp=max(temp,dfs(tx,ty,xb,yb,typ^1,dep+1));
    42         }
    43     }
    44     return ret[xw][yw][xb][yb][dep][typ]=temp+1;
    45 }
    46 int main()
    47 {
    48     scanf("%d%d%d%d%d",&n,&x1,&yy,&x2,&y2);
    49     if(abs(x2-x1)+abs(y2-yy)<=1){printf("WHITE 1
    ");return 0;}
    50     else printf("BLACK %d
    ",dfs(x1,yy,x2,y2,0,1));
    51     return 0;
    52 }

    当然,这里也可以使用$alpha$-$eta$剪枝,其原理如下:

    考虑一个黑棋下的局面,我们知道他的前一手和后一手都是白棋要下的(废话)

    如果这个局面有一个发展使得黑棋可以以a步抓到白棋,那么这个局面黑棋取胜所需的最多步数即为$a$

    可是我们知道,对于当前局面的上一层,是一个白棋的局面,他想最大化黑棋的步数,假设白棋已经搜到的前几个局面之中黑棋最大的步数是$b$

    那我们可以发现,如果$a<b$,那么无论当前局面如何发展,白棋都不会容许走到当前局面(思考一下:当双方均采用最优策略时,如果进入这个局面,黑棋至多只需要$a$步就能取胜,而如果不进入这个局面,黑棋至少需要$b$步才能取胜,所以白棋一定不会允许黑棋进入这个局面,也即我们不再需要了解这个局面接下来会如何演变,因为白棋一定不会选择这个局面!)

    于是同理考虑一个白棋下的情况,同样进行剪枝就可以了

     1 #include <cstdio>
     2 #include <cmath>
     3 #include <cstring>
     4 #include <cstdlib>
     5 #include <iostream>
     6 #include <algorithm>
     7 #include <queue>
     8 #include <stack>
     9 using namespace std;
    10 const int inf=1e9;
    11 int x1,x2,yy,y2;
    12 int n;
    13 int to[9][2]={{0,0},{1,0},{0,1},{-1,0},{0,-1},{2,0},{0,2},{-2,0},{0,-2}};
    14 int ret[21][21][21][21][61][2];
    15 bool used[21][21][21][21][61][2];
    16 bool check(int x,int y)
    17 {
    18     return x>0&&x<=n&&y>0&&y<=n;
    19 }
    20 int dfs(int xw,int yw,int xb,int yb,int typ,int dep,int lasxw,int lasyw,int lasxb,int lasyb)//typ=1表示黑棋走,typ=0表示白棋走
    21 {
    22     if(dep>3*n)return inf;
    23     if(ret[xw][yw][xb][yb][dep][typ]&&!used[xw][yw][xb][yb][dep][typ])return ret[xw][yw][xb][yb][dep][typ];
    24     if(xw==xb&&yw==yb)
    25     {
    26         if(typ)return inf;
    27         else return 0;
    28     }
    29     int temp=typ?inf:0;
    30     ret[xw][yw][xb][yb][dep][typ]=temp;
    31     if(typ)
    32     {
    33         for(int i=1;i<=8;i++)
    34         {
    35             int tx=xb+to[i][0],ty=yb+to[i][1];
    36             if(check(tx,ty))temp=min(temp,dfs(xw,yw,tx,ty,typ^1,dep+1,xw,yw,xb,yb));
    37             ret[xw][yw][xb][yb][dep][typ]=temp+1;
    38             if(temp+1<ret[lasxw][lasyw][lasxb][lasyb][dep-1][typ^1]&&dep!=1){used[xw][yw][xb][yb][dep][typ]=1;return temp+1;}
    39         }
    40     }else
    41     {
    42         for(int i=1;i<=4;i++)
    43         {
    44             int tx=xw+to[i][0],ty=yw+to[i][1];
    45             if(check(tx,ty))temp=max(temp,dfs(tx,ty,xb,yb,typ^1,dep+1,xw,yw,xb,yb));
    46             ret[xw][yw][xb][yb][dep][typ]=temp+1;
    47             if(temp+1>ret[lasxw][lasyw][lasxb][lasyb][dep-1][typ^1]&&dep!=1){used[xw][yw][xb][yb][dep][typ]=1;return temp+1;}
    48         }
    49     }
    50     used[xw][yw][xb][yb][dep][typ]=0;
    51     return ret[xw][yw][xb][yb][dep][typ]=temp+1;
    52 }
    53 int main()
    54 {
    55     scanf("%d%d%d%d%d",&n,&x1,&yy,&x2,&y2);
    56     if(abs(x2-x1)+abs(y2-yy)<=1){printf("WHITE 1
    ");return 0;}
    57     else printf("BLACK %d
    ",dfs(x1,yy,x2,y2,0,1,0,0,0,0));
    58     return 0;
    59 }

    其实你会发现,剪枝的版本比不剪枝的版本还要慢,究其原因,在于我们剪枝让一部分记忆化失效了,这样反而增加了用时

    如果用下面的写法就可以回避这个问题,但是由于增加了一个数组会造成mle

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    using namespace std;
    const int inf=1e9;
    int x1,x2,yy,y2;
    int n;
    int to[9][2]={{0,0},{1,0},{0,1},{-1,0},{0,-1},{2,0},{0,2},{-2,0},{0,-2}};
    int ret[21][21][21][21][61][2];
    int used[21][21][21][21][61][2];
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    int min(int x,int y)
    {
        return x<y?x:y;
    }
    bool check(int x,int y)
    {
        return x>0&&x<=n&&y>0&&y<=n;
    }
    int dfs(int xw,int yw,int xb,int yb,int typ,int dep,int lasxw,int lasyw,int lasxb,int lasyb)//typ=1表示黑棋走,typ=0表示白棋走
    {
        if(dep>3*n)return inf;
        if(ret[xw][yw][xb][yb][dep][typ]&&!used[xw][yw][xb][yb][dep][typ])return ret[xw][yw][xb][yb][dep][typ];
        if(xw==xb&&yw==yb)
        {
            if(typ)return inf;
            else return 0;
        }
        int temp=typ?inf:0;
        if(!ret[xw][yw][xb][yb][dep][typ])ret[xw][yw][xb][yb][dep][typ]=temp;
        if(typ)
        {
            for(int i=used[xw][yw][xb][yb][dep][typ]+1;i<=8;i++)
            {
                int tx=xb+to[i][0],ty=yb+to[i][1];
                if(check(tx,ty)){int t=dfs(xw,yw,tx,ty,typ^1,dep+1,xw,yw,xb,yb);temp=min(temp,t);}
                ret[xw][yw][xb][yb][dep][typ]=min(ret[xw][yw][xb][yb][dep][typ],temp+1);
                if(temp+1<ret[lasxw][lasyw][lasxb][lasyb][dep-1][typ^1]&&dep!=1){used[xw][yw][xb][yb][dep][typ]=i;return temp+1;}
            }
        }else
        {
            for(int i=used[xw][yw][xb][yb][dep][typ]+1;i<=4;i++)
            {
                int tx=xw+to[i][0],ty=yw+to[i][1];
                if(check(tx,ty)){int t=dfs(tx,ty,xb,yb,typ^1,dep+1,xw,yw,xb,yb);temp=max(temp,t);}
                ret[xw][yw][xb][yb][dep][typ]=max(ret[xw][yw][xb][yb][dep][typ],temp+1);
                if(temp+1>ret[lasxw][lasyw][lasxb][lasyb][dep-1][typ^1]&&dep!=1){used[xw][yw][xb][yb][dep][typ]=i;return temp+1;}
            }
        }
        used[xw][yw][xb][yb][dep][typ]=0;
        return ret[xw][yw][xb][yb][dep][typ];
    }
    int main()
    {
        scanf("%d%d%d%d%d",&n,&x1,&yy,&x2,&y2);
        if(abs(x2-x1)+abs(y2-yy)<=1){printf("WHITE 1
    ");return 0;}
        else printf("BLACK %d
    ",dfs(x1,yy,x2,y2,0,1,0,0,0,0));
        return 0;
    }
  • 相关阅读:
    HDU 3681 Prison Break 越狱(状压DP,变形)
    POJ 2411 Mondriaan's Dream (状压DP,骨牌覆盖,经典)
    ZOJ 3471 Most Powerful (状压DP,经典)
    POJ 2288 Islands and Bridges (状压DP,变形)
    HDU 3001 Travelling (状压DP,3进制)
    POJ 3311 Hie with the Pie (状压DP)
    POJ 1185 炮兵阵地 (状压DP,轮廓线DP)
    FZU 2204 7
    POJ 3254 Corn Fields (状压DP,轮廓线DP)
    ZOJ 3494 BCD Code (数位DP,AC自动机)
  • 原文地址:https://www.cnblogs.com/zhangleo/p/14038720.html
Copyright © 2011-2022 走看看