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

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

题意:

两人下棋,要求下棋时上面左面必须全有棋子,否则不能下,双方采用得分最优策略,询问两人得分之差。

分析:

棋盘的状态可以用一个倒着的三角形表示(因为下棋限制条件),同时因为数据范围:

可以用到 轮廓线 \(dp\)

设竖着的状态是 \(1\) ,横着的状态是 \(0\)

根据倒三角形设置初始状态:\(11111(n)000000(m)\) ,结尾状态:\(00000(n)111111(m)\) ,括号里为数字多少。

转移时, 若是要转移第几位,意思就是从 \(10\) 变成 \(01\) ,我们可以看成 \(\&3\)

同时,因为两者希望最优策略,即:

先行者希望取到自己的最大值,后行者希望对方取到最小值,因此状态转移时,先行者取 \(max\) ,后行者取 \(min\)

我们枚举每一位,询问能否变化 \(((sta>>i&3)!=1)\) ,也就是向上连接的数是 \(0\) 而不是 \(1\) . 然后根据人物,选择当前状态的 \(min/max\) 即可。

最后输出最终状态即可。

代码:

// P4363 [九省联考2018]一双木棋chess
#include<bits/stdc++.h>
using namespace std;
#define int long long 

const int N=10,inf=1e9+7;
int n,m,a[N][N],b[N][N],dp[1<<20];

int dfs(int now,bool opt,int n,int m){
    if(~dp[now]) return dp[now];//记忆化,通过-1判重
    dp[now]=opt?-inf:inf;//根据选择的人判断选取ans 最小值/最大值
    int x=n,y=0;
    for(int i=0;i<n+m-1;i++){//状态转移哪一位
        if(now>>i&1) x--; else y++;
        if((now>>i&3)!=1) continue;
        int nxt=now^(3<<i);//从[10] 变成 [01] ,就是异或3
        //minmax容斥
        if(opt) dp[now]=max(dp[now],dfs(nxt,opt^1,n,m)+a[x][y]);
        else    dp[now]=min(dp[now],dfs(nxt,opt^1,n,m)-b[x][y]);
    }
    return dp[now];
}

signed main(){
    cin>>n>>m;
    for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%lld",&a[i][j]);
    for(int i=0;i<n;i++) for(int j=0;j<m;j++) scanf("%lld",&b[i][j]);
    memset(dp,-1,sizeof(dp));
    dp[((1<<n)-1)<<m]=0;//一开始状态设为 1111111(n个)000000000(m个)
    printf("%lld\n",dfs((1<<n)-1,1,n,m));
    system("pause");
    return 0;
}
posted @ 2021-09-27 09:49  Evitagen  阅读(85)  评论(0)    收藏  举报