qwq
据说这个题有好多种乱搞的做法。
这里主要介绍的是,状压轮廓线的做法。
首先,我们会发现,已经放过的棋子和棋盘的左边界和上边界构成一个轮廓线,如果我们将向上看成是0,向下看成是1的画,那么我们的初始状态就是形如 000000111111 000000111111 000000111111,而目标状态就是形如 111111000000 111111000000 111111000000的一个东西,经过仔细5推敲,我们会发现,对于当前的一个状态来说,我们能放的棋子的位置就是一些"窝窝",其实体现到轮廓线上,就是 01 01 01,我们每次可以将一个 01 01 01变成 10 10 10,那么也就相当于在某个位置下了一个棋子了
知道这个之后呢,我们只需要根据当前的人的不同,对 d p dp dp值取 m i n 或 者 m a x min或者max min或者max
这里我们 d p dp dp值存的就是两个人的分数之差
// luogu-judger-enable-o2
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#define pb push_back
#define mk make_pair
#define ll long long
#define lson ch[x][0]
#define rson ch[x][1]
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = (1<<21)+3;
// 0 up 1 right
int a[20][20];
int b[20][20];
int f[22][maxn];
int g[22][maxn];
int n,m;
int sum;
int tmp;
int dfs(int x,int s)
{
int c[21];
int tot = ++tmp;
//cout<<"******"<<" "<<tot<<endl;
if (s==(1<<m)-1) return 0;
if (g[x][s]) return f[x][s];
g[x][s]=1;
for (int i=0;i<sum;i++) c[i] = (1<<i) & s;
//cout<<x<<" "<<s<<" "<<tot<<endl;
//for (int i=0;i<sum;i++) cout<<c[i]<<" ";
//cout<<endl;
//cout<<endl;
int ret;
x==1 ?ret = -1e9 : ret = 1e9;
int heng = n+1,line = 0;
for (int i=0;i<sum;i++)
{
if (c[i]==0) heng--;
else line++;
if (i!=0 && c[i-1]==0 && c[i])
{
//cout<<x<<" "<<s<<" "<<heng<<" "<<line<<" "<<tot<<endl;
if (x==1)
ret=max(ret,dfs(x^1,s^(1<<i-1)^(1<<(i)))+a[heng][line]);
else
ret=min(ret,dfs(x^1,s^(1<<i-1)^(1<<(i)))-b[heng][line]);
}
}
// cout<<x<<" "<<s<<" "<<ret<<" *****"<<endl;
f[x][s]=ret;
return ret;
}
int main()
{
n=read(),m=read();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) a[i][j]=read();
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) b[i][j]=read();
sum = n+m;
cout<<dfs(1,((1<<sum)-1)^((1<<n)-1));
return 0;
}