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

本文介绍了一个n*m网格上的两人游戏,玩家需选择未被占领且符合条件的格子进行占领以获得分数,通过记忆化搜索优化搜索过程,实现双方最优策略下的得分差计算。

题目描述:

有一个 n∗m的方格, Alice 和 Bob 玩游戏。每次每人可以选择一个格子占领,前提是这个格子未被占领且它左上方的所有格子都已被占领。

第 i行第 j 列的格子若被 Alice 占领则 Alice 获得 Ai,j 分,若被 Bob 占领则 Bob 获得 Bi,j分。

Alice 先手,所有格子都被占领时结束。双方都想最大化自己的得分与对方得分的差。求双方采取最优策略时 Alice 得分与 Bob 得分的差。

n,m≤10。

题目分析:

这玩意好像叫做对抗搜索.
因为A想最大化suma-sumb而B想要最小化suma-sumb
考场上就打了个50的暴力…
纯纯的搜索,枚举放的位置,按照当前的人选决策.
考虑到每行放的棋子数量肯定是递减的

……..
……
….
..
这样的形式,而且肯定是从左向右的
如果我们用一个m+1进制n位的数来储存每行放旗子的状态,那么我们就可以利用记忆化搜索来优化这个搜索.
所有的状态数 Cmn+m C n + m m 2e5左右,那么我们搞个map存结果就好了.

题目链接:

BZOJ 5248
Luogu 4363

50 Tle 代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
bool vis[12][12];
int a[12][12],b[12][12];
int col[12][12];
int n,m;
struct node{
    int ans1,ans2;
};
node dfs(int num,int f)
{
    if(num==n*m)
    {
        int ans1=0,ans2=0;
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)
          {
            if(col[i][j]==1) ans1+=a[i][j];
            if(col[i][j]==2) ans2+=b[i][j];
          }
        return (node){ans1,ans2};
    }
   node ans;
   int maxi=-1e9+7;
   for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
     {
        if(vis[i-1][j]||(i-1==0))
         if(vis[i][j-1]||(j-1==0))
          if(!vis[i][j])
          {
            vis[i][j]=1;
            col[i][j]=f;
            node dx=dfs(num+1,f==1?2:1);
            vis[i][j]=0;
            col[i][j]=0;
            int ansx=f==1?dx.ans1-dx.ans2:dx.ans2-dx.ans1;
            if(ansx>maxi) maxi=ansx,ans=dx;
          }
     }
    return ans;
}
int main()
{
    //freopen("chess.in","r",stdin);
    //freopen("chess.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      scanf("%d",&b[i][j]);
    node ans=dfs(0,1);
    printf("%d\n",ans.ans1-ans.ans2);
    return 0;
}

Ac 代码:

#include <cstdio>
#include <algorithm>
#include <map>
#define ll long long
#define inf 0x7fffffff
std::map <ll,int> mp;
ll end;
int n,m;
int num[20],a[20][20],b[20][20];
inline int unzip(ll sta)
{
    int s=0;
    for(int i=n;i;i--) s+=(num[i]=(sta%(m+1))),sta/=(m+1);
    return s&1;
}
inline ll zip()
{
    ll s=0;
    for(int i=1;i<=n;i++) s=s*(m+1)+num[i];
    return s;
}
int DFS(ll sta)
{
    if(mp.find(sta)!=mp.end()) return mp[sta];
    if(sta==end) return 0;
    int opt=unzip(sta);
    int ans=opt?inf:-inf;
    if(num[1]<m)
    {
        ++num[1];
        if(opt) ans=std::min(ans,DFS(zip())-b[1][num[1]]);
        else ans=std::max(ans,DFS(zip())+a[1][num[1]]);
        --num[1];
    }
    for(int i=2;i<=n;i++)
     if(num[i-1]>num[i])
     {
        ++num[i];
        if(opt) ans=std::min(ans,DFS(zip())-b[i][num[i]]);
        else ans=std::max(ans,DFS(zip())+a[i][num[i]]);
        --num[i];
     }
    return mp[sta]=ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
     for(int j=1;j<=m;j++)
      scanf("%d",&b[i][j]);
    for(int i=1;i<=n;i++) num[i]=m;
    end=zip();
    DFS(0);
    printf("%d\n",mp[0]);
    return 0;
}
# P4363 [九省联考 2018] 一双 chess ## 题目描述 菲菲和牛牛在一块 $n$ 行 $m$ 列的盘上下,菲菲执黑先手,牛牛执白后手。 局开始时,盘上没有任何子,两人轮流在格子上落子,直到填满盘时结束。 落子的规则是:一个格子可以落子当且仅当这个格子内没有子且这个格子的左侧及上方的所有格子内都有子。 盘的每个格子上,都写有两个非负整数,从上到下第 $i$ 行中从左到右第 $j$ 列的格子上的两个整数记作 $a_{i,j}$ 和 $b_{i,j}$。 在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑的格子上的 $a_{i,j}$ 之和,牛牛的得分是所有有白的格子上的 $b_{i,j}$ 的和。 菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何? ## 输入格式 第一行有两个整数,分别表示盘的行数 $n$ 和列数 $m$。 第 $2$ 到第 $(n + 1)$ 行,每行 $m$ 个整数,第 $(i + 1)$ 行的第 $j$ 个整数表示 $a_{i, j}$。 第 $(n + 2)$ 到第 $(2n + 1)$ 行,每行 $m$ 个整数,第 $(n + i + 1)$ 行的第 $j$ 个整数表示 $b_{i, j}$。 ## 输出格式 输出一行一个整数,表示菲菲的得分减去牛牛的得分的结果。 ## 输入输出样例 #1 ### 输入 #1 ``` 2 3 2 7 3 9 1 2 3 7 2 2 3 1 ``` ### 输出 #1 ``` 2 ``` ## 说明/提示 ### 样例 1 说明 ![](https://cdn.luogu.com.cn/upload/pic/16877.png) 盘如图所示,双方都采用最优策略时,局如下: - 菲菲下在第 $1$ 行第 $1$ 列(这是第一步时唯一可以落子的格子)。 - 牛牛下在第 $1$ 行第 $2$ 列。 - 菲菲下在第 $2$ 行第 $1$ 列。 - 牛牛下在第 $1$ 行第 $3$ 列。 - 菲菲下在第 $2$ 行第 $2$ 列。 - 牛牛下在第 $2$ 行第 $3$ 列(这是这一步时唯一可以落子的格子)。 - 填满盘,游戏结束。 盘面如下: ![](https://cdn.luogu.com.cn/upload/pic/16878.png) 菲菲的得分为 $2 + 9 + 1 = 12$,牛牛的得分为 $7 + 2 + 1 = 10$。 ### 数据规模与约定 各测试点信息如下表。 ![](https://cdn.luogu.com.cn/upload/pic/16879.png) - 对于编号为奇数的测试点,保证 $b_{i, j} = 0$。 - 对于全部的测试点,保证 $1 \leq n, m \leq 10$,$0 \leq a_{i, j}, b_{i, j} \leq 10^5$。 思路:先写m个0,然后看每一行,有几个子就在第几个0后面插入一个1。为什么WA了 ```cpp #include <iostream> using namespace std; const int INF = 0x3f3f3f3f; const int MAX_N = 15; int n, m, dp[1 << 20]; int a[MAX_N][MAX_N]; int b[MAX_N][MAX_N]; bool first(int s) { int f = 0; for (int j = 0, k = 0; k < n + m; k++) if (s >> k & 1) f ^= j; else j++; return (f & 1) ^ 1; } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> a[i][j]; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> b[i][j]; for (int s = 1 << n; s <= (1 << n) - 1 << m - 1; s++) { if (__builtin_popcount(s) != n) continue; int f = first(s); dp[s] = f ? -INF : INF; bool flag = false; for (int i = 0, j = 0, k = 0; k < n + m; k++) if (s >> k & 1) { i++; if (flag) { int p = s ^ (1 << k) ^ (1 << k - 1); if (f) dp[s] = max(dp[s], dp[p] + a[i][j]); else dp[s] = min(dp[s], dp[p] - b[i][j]); flag = false; } } else j++, flag = true; } cout << dp[(1 << n) - 1 << m - 1] << '\n'; return 0; } ```
最新发布
09-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值