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

42 篇文章 0 订阅
16 篇文章 0 订阅

题目描述:

有一个 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值