bzoj5248 [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
 
考虑到n,m都很小
所以我们可以维护轮廓线
一开始我有一个naive的想法
f[s]表示轮廓线状态为s时能取得的最优解,当前为先手取的话取max,后收取的话取min
但是我发现这么做好像不对
想了想改成f[s]表示轮廓线状态为s时能取得的最优解,当前为先手取的话取min,后收取的话取max
发现还是不对
这样做不能决定是谁的最优
所以定义f[s]为状态为s时最多/最少还能得到的多少分数
然后就可以瞎**dp了
//%std
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
#define lovelive long long
#define lc son[x][0]
#define rc son[x][1]
#define lowbit(x) (x&(-x))
#define pt vc
void read(int &x)
{
  int p=1;
  x=0;
  char c=getchar();
  while(c<'0'||c>'9')
  {
    if(c=='-')
      p=-1;
    c=getchar();
  }
  while(c>='0'&&c<='9')
  {
      x=x*10+c-48;
      c=getchar();
  }
  x*=p;
}
map<lovelive,int>  sp;
int f[200020];
lovelive a[200020],tot;
int A[2][11][11],n,m;
void dfs(int i,lovelive j,lovelive lim,lovelive pre)
{
  if(i==n)
  {
      pre=pre*lim+j;
      a[++tot]=pre;
      sp[pre]=tot;
    return; 
  }
  for(int k=0;k<=j;k++)
    dfs(i+1,j-k,lim,pre*lim+k);
}
lovelive pw[20];
bool calc(lovelive x)
{
  int sum=m; 
  int now=0;
  for(int j=0;j<n;j++)
  {
      sum-=x%(m+1);
      now^=sum;
      x/=(m+1);
  }
  return now&1;
}
int main()
{
//  freopen("chess.in","r",stdin);
//  freopen("chess.out","w",stdout);
  int now=0,sum;
  lovelive x,y,tmp;
  read(n);read(m);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
      read(A[0][i][j]);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
      read(A[1][i][j]),A[1][i][j]*=-1;
  pw[0]=1;
  for(int i=1;i<=n;i++)
    pw[i]=pw[i-1]*(lovelive)(m+1);
  dfs(0,m,m+1,0);
  for(int i=1;i<=tot;i++)
  {
    if(!calc(a[i]))
      f[i]=-1e9;
    else
      f[i]=1e9;
  }
  f[1]=0;
  for(int i=1;i<=tot;i++)
  {
//    if(f[i]!=0)
//      i--,i++;
      x=y=a[i];
    now=calc(a[i]);
    sum=m;
      for(int j=0;j<n;j++)
      {
        sum-=x%(m+1);
//        if(y-pw[j]+pw[j+1]==23579400)
//          i--,i++;
        if(x%(m+1)!=0)
        {
            if(now)
            f[sp[y-pw[j]+pw[j+1]]]=max(f[sp[y-pw[j]+pw[j+1]]],f[i]+A[now][j+1][sum+1]);
          else
            f[sp[y-pw[j]+pw[j+1]]]=min(f[sp[y-pw[j]+pw[j+1]]],f[i]+A[now][j+1][sum+1]);
        }
        x/=(m+1);
    }
  }
  cout<<f[sp[m*pw[n]]]<<"
";
  return 0;
} 
/*
2 3
2 7 3
9 1 2
3 7 2
2 3 1

10 10
0 0 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0

*/ 
View Code
原文地址:https://www.cnblogs.com/NicoDafaGood/p/8868190.html