bzoj5248[2018多省省队联测]一双木棋

http://www.elijahqi.win/archives/2958
Description

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

Input

第一行包含两个正整数n,m,保证n,m≤10。
接下来n行,每行m个非负整数,按从上到下从左到右的顺序描述每个格子上的
第一个非负整数:其中第i行中第j个数表示Aij。
接下来n行,每行m个非负整数,按从上到下从左到右的顺序描述每个格子上的
第二个非负整数:其中第i行中第j个数表示Bij
n, m ≤ 10 , Aij, Bij ≤ 100000

Output

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

Sample Input

2 3
2 7 3
9 1 2
3 7 2
2 3 1
Sample Output

2
HINT

跪膜icefox orz我只想出50分做法

设dp[0/1][s][0/1]分别表示当前谁是先手 我的状态是多少 处于当前状态下先手的总得分和后手的总得分 因为观察到每个人都是必胜策略 那么他们可以预知未来也就是他们可以根据以后的得分情况决定这次走哪一步于是记忆化搜索 这个状态表示每一行的每一列有多少个数 用一个n进制压起来 因为10^10会超过long long 所以直接用一个hash来记录状态即可 复杂度加上剪枝应该是正确的因为枚举不超过 C{20,10}可以考虑轮廓线的那种状压dp证明 我每条轮廓线唯一确定种状态 那么我就是在20种可能的形态中选出10种形态

#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#define mod 1000007 
#define ll long long
#define pa pair<ll,int>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if(T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
vector<pa> hs[mod+10];int cnt;ll p[11];
inline int query(ll x){static int tmp1,i;tmp1=x%mod;
    for (i=0;i<hs[tmp1].size();++i){
        if (hs[tmp1][i].first==x) return hs[tmp1][i].second;
    }hs[tmp1].push_back(make_pair(x,++cnt));
    return cnt;
}
int dp[2][200020][2],n,m,a[2][11][11];bool flag[200020][2];
inline void dfs(ll s,int op){int st[11];int ss=query(s);memset(st,0,sizeof(st));
    if (flag[ss][op]) return;flag[ss][op]=1;bool flag1=1;
    st[0]=inf;for (int i=1;i<=n;++i) {st[i]=s/p[i-1]%(m+1);
    if (st[i]<m) flag1=0;}if (flag1) return;int tt=0,t1=0,t2=0;
    for (int i=1;i<=n;++i){ll tmp=s;
        if (st[i-1]<st[i]+1||st[i]==m)continue; 
        tmp-=p[i-1]*st[i],tmp+=p[i-1]*(st[i]+1);
        int s1=query(tmp);dfs(tmp,op^1);dp[op^1][s1][op]+=a[op][i][st[i]+1];
        if (!tt) {tt=dp[op^1][s1][op]-dp[op^1][s1][op^1],t1=dp[op^1][s1][op],
        t2=dp[op^1][s1][op^1];dp[op^1][s1][op]-=a[op][i][st[i]+1];continue;}
        if (dp[op^1][s1][op]-dp[op^1][s1][op^1]>tt)
        t1=dp[op^1][s1][op],t2=dp[op^1][s1][op^1],tt=dp[op^1][s1][op]-dp[op^1][s1][op^1];
        dp[op^1][s1][op]-=a[op][i][st[i]+1];
    }dp[op][ss][op]=t1;dp[op][ss][op^1]=t2;
}
int main(){
    freopen("chess.in","r",stdin);
    n=read();m=read();p[0]=1;
    for (int i=1;i<=n;++i) p[i]=p[i-1]*(m+1);
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j) a[0][i][j]=read();
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j) a[1][i][j]=read();
    dfs(0,0);
    printf("%d\n",dp[0][1][0]-dp[0][1][1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值