题目背景
无
题目描述
将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的两部分中的任意一块继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的平方和最小。
请编程对给出的棋盘及n,求出平方和的最小值。
输入格式
第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
输出格式
仅一个数,为平方和。
输入输出样例
3 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 3
1460
思路
作者: I_AM_HelloWord
嗯,看数据范围这么小,肯定搞一堆循环暴力dp。
推式子应该不是太难,设dp[k][i][j][p][q]表示从(i,j)到(p,q)中分成k个矩形最小的平方和。
那么初始化就是dp[1][i][j][p][q]=sum(i,j,p,q)^2
至于,sum可以用一个二维前缀和的预处理搞。
考虑一下具体的dp转移过程:
再枚举一个t,表示把i到p行从t行这个分成两块,一块分一次,一块分k-1次,
也表示把j到q列从t列分成两块,一块分一次,一块分k-1次
那么整个方程就很清晰了:
ChkMin(dp[tk][i][j][p][q],min(dp[tk-1][i][j][t-1][q]+dp[1][t][j][p][q],dp[1][i][j][t-1][q]+dp[tk-1][t][j][p][q]));
ChkMin(dp[tk][i][j][p][q],min(dp[tk-1][i][j][p][t-1]+dp[1][i][t][p][q],dp[1][i][j][p][t-1]+dp[tk-1][i][t][p][q]));
代码:
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=8; int n=8,k; int a[N][N],s[N][N]; int dp[2*N][N][N][N][N]; int main () { memset(dp,0x3f,sizeof(dp)); scanf("%d",&k); for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) scanf("%d",&a[i][j]); for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]; for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) for(int p=i; p<=n; p++) for(int q=j; q<=n; q++) { int t=s[p][q]-s[p][j-1]-s[i-1][q]+s[i-1][j-1]; dp[1][i][j][p][q]=t*t; } for(int tk=2; tk<=k; tk++) for(int i=n; i>=1; i--) for(int j=n; j>=1; j--) for(int p=i; p<=n; p++) for(int q=j; q<=n; q++) { for(int t=i+1; t<=p; t++) dp[tk][i][j][p][q]=min(dp[tk][i][j][p][q],min(dp[tk-1][i][j][t-1][q]+dp[1][t][j][p][q],dp[1][i][j][t-1][q]+dp[tk-1][t][j][p][q])); for(int t=j+1; t<=q; t++) dp[tk][i][j][p][q]=min(dp[tk][i][j][p][q],min(dp[tk-1][i][j][p][t-1]+dp[1][i][t][p][q],dp[1][i][j][p][t-1]+dp[tk-1][i][t][p][q])); } printf("%d\n",dp[k][1][1][n][n]); return 0; }