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; }