题解 益智游戏
还真是益智游戏(
人类智慧发现合法的图一定长这样:

于是对着中间那一段做区间 DP 并大力前缀和优化可以有 65 pts
然后正解:
换个方法人类智慧
整个图中只有两种方格的部分分是容易的

考虑这样一种划分:

枚举两条折线的交点,然后就注意到左上、右下、右上、左下的子矩阵都只会包含两种特定的摆放方式,就可以魔改算法 1 了
具体的,因为矩形是在扩张的,所以令 \(f_{i, j}\) 为在第 \(i\) 行,分界点在 \(j\),仅考虑 \(j\) 以左贡献的最小贡献
其它方向是类似的
复杂度 \(O(n^2)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 2010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
ll a[N][N];
int c[N][N];
// namespace force{
// ll ans=INF;
// bool vis[N][N];
// int id[N][N], tem[N], tot, lim;
// void dfs(int u) {
// if (u>tot) {
// ll sum=0;
// for (int i=1; i<=n; ++i)
// for (int j=1; j<=n; ++j)
// if (tem[id[i][j]]!=c[i][j])
// sum+=a[i][j];
// for (int i=1; i<=lim; ++i) for (int j=1; j<=lim; ++j) vis[i][j]=0;
// for (int i=1; i<=n; ++i) {
// for (int j=1; j<=n; ++j) {
// if (tem[id[i][j]]==1)
// vis[i*2-1][j*2-1]=vis[i*2-1][j*2]=1;
// else if (tem[id[i][j]]==2)
// vis[i*2-1][j*2]=vis[i*2][j*2]=1;
// else if (tem[id[i][j]]==3)
// vis[i*2][j*2-1]=vis[i*2][j*2]=1;
// else vis[i*2-1][j*2-1]=vis[i*2][j*2-1]=1;
// }
// }
// for (int i=1; i<=lim; ++i)
// for (int j=1; j<=lim; ++j) if (vis[i][j])
// if (vis[i-1][j-1]||vis[i+1][j-1]||vis[i-1][j+1]||vis[i+1][j+1])
// return ;
// ans=min(ans, sum);
// return ;
// }
// for (int i=1; i<=4; ++i) tem[u]=i, dfs(u+1);
// }
// void solve() {
// lim=n<<1;
// for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) id[i][j]=++tot;
// dfs(1);
// printf("%lld\n", ans);
// }
// }
// namespace task1{
// #define M 305
// ll f[M][M][M], g[M][M][M], ans=INF;
// ll left[M][M], up[M][M], down[M][M], right[M][M];
// void solve() {
// // cout<<double(sizeof(f)*2+sizeof(tem))/1000/1000<<endl;
// memset(f, 0x3f, sizeof(f));
// memset(g, 0x3f, sizeof(g));
// for (int i=1; i<=n+1; ++i) for (int j=i-1; j<=n; ++j) f[0][i][j]=0;
// for (int i=1; i<=n+1; ++i) for (int j=i-1; j<=n; ++j) g[n+1][i][j]=0;
// for (int i=1; i<=n; ++i) {
// for (int j=1; j<=n; ++j) {
// left[i][j]=(c[i][j]!=4?a[i][j]:0);
// up[i][j]=(c[i][j]!=1?a[i][j]:0);
// right[i][j]=(c[i][j]!=2?a[i][j]:0);
// down[i][j]=(c[i][j]!=3?a[i][j]:0);
// }
// for (int j=1; j<=n; ++j) {
// left[i][j]+=left[i][j-1];
// up[i][j]+=up[i][j-1];
// down[i][j]+=down[i][j-1];
// }
// for (int j=n; j; --j) right[i][j]+=right[i][j+1];
// }
// for (int i=1; i<=n; ++i)
// for (int j=1; j<=n+1; ++j)
// for (int k=j-1; k<=n; ++k)
// for (int l=1; l<=j; ++l)
// for (int r=k; r<=n; ++r) {
// f[i][j][k]=min(f[i][j][k], f[i-1][l][r]+left[i][j-1]+up[i][k]-up[i][j-1]+right[i][k+1]);
// // printf("f[%d][%d][%d]=%lld\n", i, j, k, f[i][j][k]);
// }
// for (int i=n; i; --i)
// for (int j=1; j<=n+1; ++j)
// for (int k=j-1; k<=n; ++k)
// for (int l=1; l<=j; ++l)
// for (int r=k; r<=n; ++r) {
// g[i][j][k]=min(g[i][j][k], g[i+1][l][r]+left[i][j-1]+down[i][k]-down[i][j-1]+right[i][k+1]);
// // printf("g[%d][%d][%d]=%lld\n", i, j, k, g[i][j][k]);
// }
// for (int i=0; i<=n; ++i)
// for (int l1=1; l1<=n+1; ++l1)
// for (int r1=l1-1; r1<=n; ++r1)
// for (int l2=1; l2<=n+1; ++l2)
// for (int r2=l2-1; r2<=n; ++r2)
// if (l1-1<r2+1 && l2-1<r1+1)
// ans=min(ans, f[i][l1][r1]+g[i+1][l2][r2]);
// printf("%lld\n", ans);
// }
// }
namespace task2{
#define M 305
ll tem[M][M], pre[M], suf[M];
ll f[M][M][M], g[M][M][M], ans=INF;
ll left[M][M], up[M][M], down[M][M], right[M][M];
void solve() {
// cout<<double(sizeof(f)*2+sizeof(tem)*5+sizeof(pre)*2)/1000/1000<<endl;
memset(f, 0x3f, sizeof(f));
memset(g, 0x3f, sizeof(g));
memset(tem, 0x3f, sizeof(tem));
for (int i=1; i<=n+1; ++i) for (int j=i-1; j<=n; ++j) f[0][i][j]=0;
for (int i=1; i<=n+1; ++i) for (int j=i-1; j<=n; ++j) g[n+1][i][j]=0;
for (int i=1; i<=n; ++i) {
for (int j=1; j<=n; ++j) {
left[i][j]=(c[i][j]!=4?a[i][j]:0);
up[i][j]=(c[i][j]!=1?a[i][j]:0);
right[i][j]=(c[i][j]!=2?a[i][j]:0);
down[i][j]=(c[i][j]!=3?a[i][j]:0);
}
for (int j=1; j<=n; ++j) {
left[i][j]+=left[i][j-1];
up[i][j]+=up[i][j-1];
down[i][j]+=down[i][j-1];
}
for (int j=n; j; --j) right[i][j]+=right[i][j+1];
}
for (int i=1; i<=n; ++i) {
for (int l=1; l<=n+1; ++l) {
for (int r=l-1; r<=n; ++r) tem[l][r]=f[i-1][l][r];
for (int r=n; r>=l-1; --r) tem[l][r]=min(tem[l][r], tem[l][r+1]);
}
for (int r=0; r<=n; ++r)
for (int l=1; l<=r+1; ++l)
tem[l][r]=min(tem[l][r], tem[l-1][r]);
for (int j=1; j<=n+1; ++j)
for (int k=j-1; k<=n; ++k)
f[i][j][k]=tem[j][k]+left[i][j-1]+up[i][k]-up[i][j-1]+right[i][k+1];
}
for (int i=n; i; --i) {
for (int l=1; l<=n+1; ++l) {
for (int r=l-1; r<=n; ++r) tem[l][r]=g[i+1][l][r];
for (int r=n; r>=l-1; --r) tem[l][r]=min(tem[l][r], tem[l][r+1]);
}
for (int r=0; r<=n; ++r)
for (int l=1; l<=r+1; ++l)
tem[l][r]=min(tem[l][r], tem[l-1][r]);
for (int j=1; j<=n+1; ++j)
for (int k=j-1; k<=n; ++k)
g[i][j][k]=tem[j][k]+left[i][j-1]+down[i][k]-down[i][j-1]+right[i][k+1];
}
for (int i=0; i<=n; ++i) {
for (int i=0; i<=n+1; ++i) pre[i]=suf[i]=INF;
for (int l=1; l<=n+1; ++l) {
for (int r=l-1; r<=n; ++r) {
pre[r]=min(pre[r], g[i+1][l][r]);
suf[l]=min(suf[l], g[i+1][l][r]);
}
}
for (int l=1; l<=n+1; ++l) {
for (int r=l-1; r<=n; ++r) tem[l][r]=g[i+1][l][r];
for (int r=n; r>=l-1; --r) tem[l][r]=min(tem[l][r], tem[l][r+1]);
}
for (int r=0; r<=n; ++r)
for (int l=1; l<=r+1; ++l)
tem[l][r]=min(tem[l][r], tem[l-1][r]);
for (int l=1; l<=n+1; ++l) {
ll tem1=suf[l], tem2=pre[l-1];
for (int r=l-1; r<=n; ++r) {
// for (int j=l; j<=r+1; ++j) ans=min(ans, f[i][l][r]+suf[j]);
// for (int j=l-1; j<=r; ++j) ans=min(ans, f[i][l][r]+pre[j]);
tem1=min(tem1, suf[r+1]);
tem2=min(tem2, pre[r]);
ans=min(ans, f[i][l][r]+tem1);
ans=min(ans, f[i][l][r]+tem2);
ans=min(ans, f[i][l][r]+tem[l][r]);
}
}
}
printf("%lld\n", ans);
}
}
namespace task3{
ll f[N][2][N], ans=INF;
ll tem[N][N], pre[N], suf[N];
ll left[N][N], up[N][N], down[N][N], right[N][N];
void solve() {
// cout<<double(sizeof(f)+sizeof(tem)*5+sizeof(pre)*2)/1000/1000<<endl;
memset(f, 0x3f, sizeof(f));
memset(tem, 0x3f, sizeof(tem));
for (int j=1-1; j<=n; ++j) f[0][1][j]=0;
for (int i=1; i<=n; ++i) {
for (int j=1; j<=n; ++j) {
left[i][j]=(c[i][j]!=4?a[i][j]:0);
up[i][j]=(c[i][j]!=1?a[i][j]:0);
right[i][j]=(c[i][j]!=2?a[i][j]:0);
down[i][j]=(c[i][j]!=3?a[i][j]:0);
}
for (int j=1; j<=n; ++j) {
left[i][j]+=left[i][j-1];
up[i][j]+=up[i][j-1];
down[i][j]+=down[i][j-1];
}
for (int j=n; j; --j) right[i][j]+=right[i][j+1];
}
for (int i=1; i<=n; ++i) {
for (int l=1; l<=1; ++l) {
for (int r=l-1; r<=n; ++r) tem[l][r]=f[i-1][l][r];
for (int r=n; r>=l-1; --r) tem[l][r]=min(tem[l][r], tem[l][r+1]);
}
for (int r=0; r<=n; ++r)
for (int l=1; l<=1; ++l)
tem[l][r]=min(tem[l][r], tem[l-1][r]);
for (int j=1; j<=1; ++j)
for (int k=j-1; k<=n; ++k)
f[i][j][k]=tem[j][k]+left[i][j-1]+up[i][k]-up[i][j-1]+right[i][k+1];
}
for (int l=1; l<=1; ++l)
for (int r=l-1; r<=n; ++r)
ans=min(ans, f[n][l][r]);
printf("%lld\n", ans);
}
}
namespace task{
ll f[2][2][N][N], tem[2][2][N], ans=INF;
ll up[N][N], down[N][N], left[N][N], right[N][N];
void solve() {
memset(f, 0x3f, sizeof(f));
// memset(tem1, 0x3f, sizeof(tem1));
// memset(tem2, 0x3f, sizeof(tem2));
for (int i=0; i<=n; ++i) f[0][0][0][i]=f[0][1][0][i]=0;
for (int i=0; i<=n; ++i) f[1][0][n+1][i]=f[1][1][n+1][i]=0;
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) up[i][j]=up[i-1][j]+(c[i][j]!=1?a[i][j]:0);
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) left[i][j]=left[i][j-1]+(c[i][j]!=4?a[i][j]:0);
for (int i=1; i<=n; ++i) for (int j=n; ~j; --j) right[i][j]=right[i][j+1]+(c[i][j]!=2?a[i][j]:0);
for (int i=n; i; --i) for (int j=1; j<=n; ++j) down[i][j]=down[i+1][j]+(c[i][j]!=3?a[i][j]:0);
for (int i=1; i<=n; ++i) {
ll tem=INF;
for (int j=0; j<=n; ++j) {
tem=min(tem, f[0][0][i-1][j]);
f[0][0][i][j]=left[i][j]+tem;
tem+=up[i-1][j+1];
}
}
// ll ans=INF, sum=0;
// for (int i=n; ~i; --i) {
// sum+=up[n][i+1];
// ans=min(ans, sum+f[0][0][n][i]);
// }
for (int i=1; i<=n; ++i) {
ll tem=INF;
for (int j=n; ~j; --j) {
tem=min(tem, f[0][1][i-1][j]);
f[0][1][i][j]=right[i][j+1]+tem;
tem+=up[i-1][j];
}
}
// ll ans=INF, sum=0;
// for (int i=0; i<=n; ++i) {
// sum+=up[n][i];
// ans=min(ans, sum+f[0][1][n][i]);
// }
for (int i=n; i; --i) {
ll tem=INF;
for (int j=0; j<=n; ++j) {
tem=min(tem, f[1][0][i+1][j]);
f[1][0][i][j]=left[i][j]+tem;
tem+=down[i+1][j+1];
}
}
// ll ans=INF, sum=0;
// for (int i=n; ~i; --i) {
// sum+=down[1][i+1];
// ans=min(ans, sum+f[1][0][1][i]);
// }
for (int i=n; i; --i) {
ll tem=INF;
for (int j=n; ~j; --j) {
tem=min(tem, f[1][1][i+1][j]);
f[1][1][i][j]=right[i][j+1]+tem;
tem+=down[i+1][j];
}
}
// ll ans=INF, sum=0;
// for (int i=0; i<=n; ++i) {
// sum+=down[1][i];
// ans=min(ans, sum+f[1][1][1][i]);
// }
// for (int i=0; i<=n; ++i) {
// for (int j=0; j<=n; ++j) tem1[j]=min((j?tem1[j-1]:0)+up[i][j]+down[i+1][j], f[0][0][i][j]+f[1][0][i+1][j]);
// for (int j=n; ~j; --j) tem2[j]=min(tem2[j+1]+up[i][j+1]+down[i+1][j+1], f[0][1][i][j]+f[1][1][i+1][j]);
// for (int j=0; j<=n; ++j) ans=min(ans, tem1[j]+tem2[j+1]+up[i][j+1]+down[i+1][j+1]);
// }
for (int i=0; i<=n; ++i) {
for (int j=0; j<=n; ++j) tem[0][0][j]=min((j?tem[0][0][j-1]:0)+up[i][j], f[0][0][i][j]);
for (int j=0; j<=n; ++j) tem[1][0][j]=min((j?tem[1][0][j-1]:0)+down[i+1][j], f[1][0][i+1][j]);
for (int j=n; ~j; --j) tem[0][1][j]=min(tem[0][1][j+1]+up[i][j+1], f[0][1][i][j]);
for (int j=n; ~j; --j) tem[1][1][j]=min(tem[1][1][j+1]+down[i+1][j+1], f[1][1][i+1][j]);
for (int j=0; j<=n; ++j) {
// cout<<"at: "<<i<<' '<<j<<endl;
// cout<<"val: "<<tem[0][0][j]<<' '<<tem[1][0][j]<<' '<<tem[0][1][j]<<' '<<tem[1][1][j]<<" = "<<tem[0][0][j]+tem[1][0][j]+tem[0][1][j]+tem[1][1][j]<<endl;
ans=min(ans, tem[0][0][j]+tem[1][0][j]+tem[0][1][j]+tem[1][1][j]);
}
}
printf("%lld\n", ans);
}
}
signed main()
{
freopen("game.in", "r", stdin);
freopen("game.out", "w", stdout);
n=read();
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) c[i][j]=read();
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) a[i][j]=read();
// force::solve();
// task1::solve();
// if (n<=300) task2::solve();
// else task3::solve();
task::solve();
return 0;
}