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

题目

题目描述
菲菲和牛牛在一块n 行m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。

落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。

棋盘的每个格子上,都写有两个非负整数,从上到下第i 行中从左到右第j 列的格 子上的两个整数记作A_{i,j}A
i,j

、B_{i,j}B
i,j

。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的A_{i,j}A
i,j

之和,牛牛的得分是所有有白棋的格子上的B_{i,j}B
i,j

的和。

菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。

输入格式
从文件chess.in 中读入数据。

输入第一行包含两个正整数n;m,保证n;m <= 10。

接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第一个非负整数:其中第i 行中第j 个数表示A_{i,j}A
i,j

接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第二个非负整数:其中第i 行中第j 个数表示B_{i,j}B
i,j

输出格式
输出到文件chess.out 中。

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

输入输出样例
输入 #1复制
2 3
2 7 3
9 1 2
3 7 2
2 3 1
输出 #1复制
2
说明/提示
样例1说明:

棋盘如图所示,双方都采用最优策略时,棋局如下:

• 菲菲下在第1 行第1 列(这是第一步时唯一可以落子的格子);

• 牛牛下在第1 行第2 列;

• 菲菲下在第2 行第1 列;

• 牛牛下在第1 行第3 列;

• 菲菲下在第2 行第2 列;

• 牛牛下在第2 行第3 列(这是这一步时唯一可以落子的格子);

• 填满棋盘,游戏结束,盘面如下。

菲菲的得分为:2 + 9 + 1 = 12 ;牛牛的得分为:7 + 2 + 1 = 10 。

对于所有的测试数据,n;m <= 10 ,A_{i,j}A
i,j

; B_{i,j}B
i,j

<= 100000。

对于编号为奇数的测试点,保证所有的B_{i,j}B
i,j

= 0 。

思路

显然放下的棋子构成一个阶梯型。

我们爆搜一发状态量,发现只有35万多种

然后在用map记录答案的情况下一发min-max对抗搜索就好了。

代码

#include<bits/stdc++.h>
#define N 400005
#define int long long
#define mod 1000000007
using namespace std;

int head[N];
int cnt,s,t;
int n,m,tot;
int qp[N][15];
int f[N],fz[15];
int deg[N],num[N];
int a[15][15],b[15][15];

map<int,int> mp;
queue<int> topo;

struct Edge{
    int to,nxt,disa,disb;
}edge[N*10];

void add(int x,int y,int z,int p){
    edge[++cnt].to=y;
    edge[cnt].nxt=head[x];
    edge[cnt].disa=z;
    edge[cnt].disb=p;
    head[x]=cnt;
}

void hsh(int x){
    int d=0;tot++;
    for(int i=1;i<=n;i++)
        d=d*15+fz[i],d%=mod,qp[tot][i]=fz[i];
    mp[d]=tot;
    num[tot]=x;
    if(num[tot]&1) f[tot]=2e18;
    else f[tot]=-2e18;
}

void dfs(int now,int lim,int num){
    if(now>n){
        hsh(num);
        return;
    }
    for(int i=0;i<=lim;i++)
        fz[now]=i,dfs(now+1,i,num+i);
}

void _find(){
    int x=0,y=0;
    for(int i=1;i<=n;i++) y=y*15+m,y%=mod;
    s=mp[x],t=mp[y];
}

void read(int &x){
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}

signed main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++) read(a[i][j]);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++) read(b[i][j]);
    }
    dfs(1,m,0);
    _find();
    f[t]=0;
    for(int i=2;i<=tot;i++){
        for(int j=n;j;j--){
            if(qp[i][j]==qp[i][j+1]) continue;
            int x=0;int idx=j,idy=qp[i][j];
            for(int p=1;p<=n;p++){
                if(p==j) x=x*15+qp[i][j]-1,x%=mod;
                else x=x*15+qp[i][p],x%=mod;
            }
            if(num[i]&1) add(i,mp[x],a[idx][idy],0);
            else add(i,mp[x],0,b[idx][idy]);
            deg[mp[x]]++;
        }
    }
    topo.push(t);
    while(topo.size()){
        int u=topo.front();topo.pop();
        for(int i=head[u];i;i=edge[i].nxt){
            int to=edge[i].to;
            if(num[to]&1) f[to]=min(f[to],f[u]-edge[i].disb);
            else f[to]=max(f[to],f[u]+edge[i].disa);
            deg[to]--;
            if(!deg[to]) topo.push(to);
        }
    }
    printf("%lld\n",f[1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值