P4363 [九省联考2018]一双木棋chess

传送门

很神奇的一题

看一眼题目感觉是博弈论?

但是要求具体结果

好像搞不了...

发现题目有限制,保证棋子排列呈阶梯型

好像可以轮廓线DP?

不会....

然后去看题解了,DFS能过???

emmm....

因为棋子排列呈阶梯型,所以如果我们暴力枚举所有的状态可以发现合法状态只有不到40万种

然后就可以直接上min-max对抗搜索了(不懂min-max对抗搜索的请百度

但是搜索也有一些技巧,有很多重复的状态

所以我们可以把每种状态压缩一波,然后把答案放到map里,如果这个状态之前找过了,就直接用就好了

为了知道当前是取min还是max,所以还要把状态解压,判断一下

具体实现还是看代码吧

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
    return x*f;
}
const int N=17,base=11,INF=1e9+7;

int n,m;
int A[N][N],B[N][N];
int c[N];//c存当前状态
inline ll hsh()//把当前状态压缩并返回压缩后的值
{
    ll res=0;
    for(int i=1;i<=n;i++) res=res*base+c[i];
    return res;
}
inline void unzip(ll x) { for(int i=n;i;i--) c[i]=x%base,x/=base; }//把状态解压
inline bool pd()//判断当前是谁走
{
    int res=0;
    for(int i=1;i<=n;i++) res+=c[i];
    return res&1;//返回1说明是后手,0说明是先手
}
map <ll,int> mp;//存状态
int dfs(ll sta)
{
    if(mp.find(sta)!=mp.end()) return mp[sta];//如果走过就直接返回之前求的值
    unzip(sta); bool flag=pd();//解压状态并判断谁走
    int res= flag ? INF : -INF;//后手取min,先手取max
    for(int i=1;i<=n;i++)//枚举每一行
    {
        if(c[i]>=c[i-1]) continue;//判断当前行是否能落子
        c[i]++; ll nex=hsh();//求出下一状态
        if(flag) res=min(res,dfs(nex)-B[i][c[i]]);
        else res=max(res,dfs(nex)+A[i][c[i]]);
        c[i]--;//回溯
    }
    mp[sta]=res;//存一下当前状态的结果
    return res;
}
int main()
{
    n=read(); m=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) A[i][j]=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) B[i][j]=read();

    for(int i=1;i<=n;i++) c[i]=m; ll las=hsh();//把结束状态存一下
    mp[las]=0;/*结束状态值为0*/ c[0]=m;//第1行可以不考虑上一行
    dfs(0);
    printf("%d",mp[0]);
    return 0;
}

 

posted @ 2018-10-16 19:45  LLTYYC  阅读(181)  评论(0编辑  收藏  举报