• [BZOJ5248][九省联考2018]一双木棋(连通性DP,对抗搜索)


    5248: [2018多省省队联测]一双木棋

    Time Limit: 20 Sec  Memory Limit: 512 MB
    Submit: 43  Solved: 34
    [Submit][Status][Discuss]

    Description

    菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。棋局开始时,棋盘上没有任何棋子,
    两人轮流在格子上落子,直到填满棋盘时结束。落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且
    这个格子的左侧及上方的所有格子内都有棋子。
    棋盘的每个格子上,都写有两个非负整数,从上到下第i行中从左到右第j列的格子上的两个整数记作Aij、Bij。在
    游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的Aij之和,牛牛的得分是所
    有有白棋的格子上的Bij的和。
    菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都
    采用最优策略且知道对方会采用最优策略,那么,最终的结果如何

    Input

    第一行包含两个正整数n,m,保证n,m≤10。
    接下来n行,每行m个非负整数,按从上到下从左到右的顺序描述每个格子上的
    第一个非负整数:其中第i行中第j个数表示Aij。
    接下来n行,每行m个非负整数,按从上到下从左到右的顺序描述每个格子上的
    第二个非负整数:其中第i行中第j个数表示Bij
    n, m ≤ 10 , Aij, Bij ≤ 100000

    Output

    输出一个整数,表示菲菲的得分减去牛牛的得分的结果。

    Sample Input

    2 3
    2 7 3
    9 1 2
    3 7 2
    2 3 1

    Sample Output

    2

    HINT

     

    Source

    [Submit][Status][Discuss]

    这套卷子真的是CCF卷的风格。。专门克制我这种做题慢如蜗牛的人。。

    首先这个题一看范围就差不多了,不是搜索就是DP,要拿满分显然得记忆化。因为博弈的最佳决策是根据后继状态选择的,所以返回的是某个状态之后可能拉出的最大/最小分差。由于先手是想让A-B最大,后手反之,交替进行,所以我们搜索时分两种情况讨论一下,最终一定能得到最优决策下的答案。

    至于怎么存储当前状态,直接状压每行放了多少个棋子即可,状态直接转移。我起先是按照11进制存储状态的,发现跑了0.99s,后来改成16进制用位运算代替除法取模,就只要0.29s了。

    还有一种方法是连通性DP,将从左下到右上的轮廓线压进二进制里转移(0表示向上,1表示向右)。

    方法一:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define rep(i,l,r) for (int i=l; i<=r; i++)
     4 typedef long long ll;
     5 using namespace std;
     6 
     7 const int N=20,inf=1000000000,P=1000007;
     8 ll H[P],G; int H1[P];
     9 int n,m,a[N][N],b[N][N],p[N];
    10 
    11 void inc(int &x){ x++; if (x>=P) x-=P; }
    12 void Hash(ll S,int k){ int x=S%P; while (H[x]) inc(x); H[x]=S; H1[x]=k; }
    13 int Find(ll S){ int x=S%P; while (H[x]!=S && H[x]) inc(x); return H1[x]; }
    14 
    15 int get(ll S,int x){ if (!x) return m; x=n-x; while (x--) S>>=4; return S&15; }
    16 ll upd(ll S,int x){
    17     if (x==1){
    18         int tot=0;
    19         rep(i,1,n-1) p[tot++]=S&15,S>>=4; S++;
    20         while (tot--) S=(S<<4)+p[tot];
    21         return S;
    22     }
    23     int tot=0;
    24     rep(i,1,n-x) p[tot++]=S&15,S>>=4; S++;
    25     while (tot--) S=(S<<4)+p[tot];
    26     return S;
    27 }
    28 
    29 int dfs(ll S,int x){
    30     int t=Find(S); if (t) return t;
    31     if (S==G-1) return (x ? -b[n][m] : a[n][m]);
    32     if (x==0){
    33         int res=-inf;
    34         rep(i,1,n) if (get(S,i)<get(S,i-1)) res=max(res,a[i][get(S,i)+1]+dfs(upd(S,i),x^1));
    35         Hash(S,res); return res;
    36     }else{
    37         int res=inf;
    38         rep(i,1,n) if (get(S,i)<get(S,i-1)) res=min(res,dfs(upd(S,i),x^1)-b[i][get(S,i)+1]);
    39         Hash(S,res); return res;
    40     }
    41 }
    42 
    43 int main(){
    44     freopen("chess.in","r",stdin);
    45     freopen("chess.out","w",stdout);
    46     scanf("%d%d",&n,&m);
    47     rep(i,1,n) G=G*16+m;
    48     rep(i,1,n) rep(j,1,m) scanf("%d",&a[i][j]);
    49     rep(i,1,n) rep(j,1,m) scanf("%d",&b[i][j]);
    50     printf("%d
    ",dfs(0,0));
    51     return 0;
    52 }

    方法二:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define rep(i,l,r) for (int i=l; i<=r; i++)
     4 using namespace std;
     5 
     6 const int N=13,inf=1000000000;
     7 int n,m,a[N][N],b[N][N],f[1<<22],p[22]={1};
     8 
     9 int cnt(int x){ int res=0; for (; x; x>>=1) if (x&1) res++; return res; }
    10 int work(int x){
    11     int res=0,w=0;
    12     for (; x; x>>=1) if (x&1) res+=w; else w++;
    13     return res;
    14 }
    15 int valA(int v,int s){ int g=cnt(v&(p[s]-1)); return a[n-s+g][1+g]; }
    16 int valB(int v,int s){ int g=cnt(v&(p[s]-1)); return b[n-s+g][1+g]; }
    17 
    18 void solve(int S,int x){
    19     int res;
    20     if (x){
    21         res=inf;
    22         for (int i=0; i<n+m-1; i++)
    23             if (!(p[i]&S) && (p[i+1]&S))
    24                 res=min(res,f[S^p[i]^p[i+1]]-valB(S,i));
    25     }else{
    26         res=-inf;
    27         for (int i=0; i<n+m-1; i++)
    28             if (!(p[i]&S) && (p[i+1]&S))
    29                 res=max(res,f[S^p[i]^p[i+1]]+valA(S,i));
    30     }
    31     f[S]=res;
    32 }
    33 
    34 int main(){
    35     freopen("chess.in","r",stdin);
    36     freopen("chess.out","w",stdout);
    37     scanf("%d%d",&n,&m);
    38     rep(i,1,n) rep(j,1,m) scanf("%d",&a[i][j]);
    39     rep(i,1,n) rep(j,1,m) scanf("%d",&b[i][j]);
    40     p[0]=1; rep(i,1,n+m) p[i]=p[i-1]<<1;
    41     f[p[m]-1]=0;
    42     rep(i,p[m],p[n+m]-p[n])
    43         if (cnt(i)==m) solve(i,(n*m-work(i))&1);
    44     printf("%d
    ",f[p[n+m]-p[n]]);
    45     return 0;
    46 }
  • 相关阅读:
    C# 利用 Geckofx60 实现下载
    C# 线程 线程池
    C# DateTime 与 String 格式转换
    C# WPF 获取程序路径
    C# 计时器 Timer 介绍
    获取远程图片并把它保存到本地
    php sql 过滤
    PHP如何生成伪静态
    用php获取客户端IP地址的方法
    php过滤危险html代码
  • 原文地址:https://www.cnblogs.com/HocRiser/p/8742663.html
Copyright © 2020-2023  润新知