题解 益智游戏

传送门

还真是益智游戏(

人类智慧发现合法的图一定长这样:

于是对着中间那一段做区间 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;
}
posted @ 2022-06-22 07:11  Administrator-09  阅读(2)  评论(0编辑  收藏  举报