【刷题】BZOJ 5248 [2018多省省队联测]一双木棋

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

11.jpg

Solution

对于搜索,还是没有深入精髓啊
手玩几把,发现无论什么时候,棋盘的状态都一定是阶梯状
又由于 \(n\)\(m\) 很小,所以每一行只要记录放了多少棋子就行,因为它们一定是连续靠在左端的;那么对于一个棋盘的状态,就是 \(n\) 个小于等于 \(m\) 的数,于是就可以压成 \(n\)位的 \(m+1\) 进制的数
于是就可以记忆化搜索了
!!记忆化搜索,记忆的一定是未来将会有的贡献,不能把之前经过的贡献加上!!

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=12,inf=0x3f3f3f3f;
int n,m,A[MAXN][MAXN],B[MAXN][MAXN];
ll num[MAXN],ed;
std::map<ll,int> M;
template<typename T> inline void read(T &x)
{
    T data=0,w=1;
    char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    x=data*w;
}
template<typename T> inline void write(T x,char c='\0')
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
    if(c!='\0')putchar(c);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline ll f()
{
    ll res=0;
    for(register int i=1;i<=n;++i)res=res*(m+1)+num[i];
    return res;
}
inline int dfs(ll now,int p)
{
    if(M.find(now)!=M.end())return M[now];
    if(now==ed)return 0;
    ll nxt;
    int res=(p?inf:-inf);
    for(register int i=1;i<=n;++i)
        if(num[i-1]>num[i])
        {
            num[i]++;
            nxt=f();
            if(p)chkmin(res,dfs(nxt,p^1)-B[i][num[i]]);
            else chkmax(res,dfs(nxt,p^1)+A[i][num[i]]);
            num[i]--;
        }
    return M[now]=res;
}
int main()
{
    read(n);read(m);
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=m;++j)read(A[i][j]);
    for(register int i=1;i<=n;++i)
        for(register int j=1;j<=m;++j)read(B[i][j]);
    for(register int i=1;i<=n;++i)ed=ed*(m+1)+m;
    num[0]=m;
    write(dfs(0,0),'\n');
    return 0;
}

转载于:https://www.cnblogs.com/hongyj/p/8783351.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值