[九省联考2018]一双木棋——对抗式记忆化搜索

题目描述:

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

思路:

由于两个人都要采取最优策略,所以我们可以采用对抗式搜索,但是很明显会超时,所以考虑记忆化,因为上面和左边的所有格子都要有的特殊的限制,使得状态数很少,所以可以用hash或map来储存状态,假设棋子已经下满了某个图的某一部分,那么状态就是这个图的剩下的部分两人都采取最优策略菲菲的得分减去牛牛的得分,直接转移即可。

/*==================
 * Author : Ylsoi
 * Problem : chess
 * Algorithm : dfs
 * Time : 2018.4.10
 * ================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
void File(){
    freopen("chess.in","r",stdin);
    freopen("chess.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=10+10;
int a[maxn][maxn],b[maxn][maxn],ans;
int n,m,res1,res2,c[maxn][maxn],pos[maxn];
int dfs1(int k,int sum);
int dfs2(int k,int sum);
int _max(int _,int __){return _>__ ? _ : __;}
int _min(int _,int __){return _<__ ? _ : __;}
map<ll,int>s;
map<ll,bool>vis;
ll cal(){
    ll ret=0ll;
    REP(i,1,n)ret=ret*11+pos[i];
    return ret;
}
int main(){
    File();
    scanf("%d%d",&n,&m);
    REP(i,1,n)REP(j,1,m)scanf("%d",&a[i][j]);
    REP(i,1,n)REP(j,1,m)scanf("%d",&b[i][j]);
    pos[0]=inf;
    printf("%d\n",-dfs1(1,0));
    return 0;
}
int dfs1(int k,int sum){
    ll dd=cal();
    if(vis[dd])return -(sum+s[dd]);
    if(k==n*m){
        vis[dd]=1;
        s[cal()]=a[n][m];
        return -(sum+a[n][m]);
    }
    int ret=-inf,bb;
    REP(i,1,n){
        if(pos[i-1]==0 && pos[i]==0)break;
        if(pos[i]==pos[i-1])continue;
        if(pos[i]==m)continue;
        ++pos[i];
        bb=dfs2(k+1,sum+a[i][pos[i]]);
        if(bb>ret){ret=bb;s[dd]=bb-sum;}
        --pos[i];
        if(pos[i]==0)break;
    }
    vis[dd]=1;
    return -ret;
}
int dfs2(int k,int sum){
    ll dd=cal();
    if(vis[dd])return sum+s[dd];
    if(k==n*m){
        vis[dd]=1;
        s[cal()]=-b[n][m];
        return sum-b[n][m];
    }
    int ret=-inf,bb;
    REP(i,1,n){
        if(pos[i-1]==0 && pos[i]==0)break;
        if(pos[i]==pos[i-1])continue;
        if(pos[i]==m)continue;
        ++pos[i];
        bb=dfs1(k+1,sum-b[i][pos[i]]);
        if(bb>ret){ret=bb;s[dd]=-bb-sum;}
        --pos[i];
        if(pos[i]==0)break;
    }
    vis[dd]=1;
    return -ret;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值