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\n");return 0;}
50     else printf("BLACK %d\n",dfs(x1,yy,x2,y2,0,1));
51     return 0;
52 }

當然,這裏也可以使用$\alpha$-$\beta$剪枝,其原理如下:

考慮一個黑棋下的局面,我們知道他的前一手和後一手都是白棋要下的(廢話)

如果這個局面有一個發展使得黑棋可以以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\n");return 0;}
57     else printf("BLACK %d\n",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\n");return 0;}
    else printf("BLACK %d\n",dfs(x1,yy,x2,y2,0,1,0,0,0,0));
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章