P1436 棋盘分割
https://www.luogu.com.cn/problem/P1436
题意:给出一个8*8的棋盘,让我们将其分割成n块,每一块的值为(块中所有数的和)^2
求出在最优的分割方式下的最小值;
思路:这是一个分割问题,但直接考虑分割有点难,所以我们逆向考虑,将其想为填补问题,也就是说,将n块矩阵填补成一个8*8的矩阵的最小答案;
那么我们需要以下几步:
1.初始化,我们先求出二维前缀和,方便下文求出每一块的平方和
2.设定一个dp数组 (五维) 第一维为块数,剩下四维表示矩阵的左上端点坐标和右下端点坐标。
3.进行计算,我们将块数从小到大进行枚举,将考虑的矩阵也从小到大枚举,然后即可,
对于一个矩阵的分割操作,我们可以有两种,假设目前矩阵为(x1,y1)到(x2,y2);
第一种: 将矩阵进行列方面的切割,这里又可以分为两种,假设我们切在k列
1.将k+1以及后面的作为被切的矩阵
2.将k+1前面的作为被切的矩阵
第二种:同上,进行行方面的切割
代码如下:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define LL long long 5 #define inf 0x3f3f3f3f 6 using namespace std; 7 int m,a[20][20],sum[20][20]; 8 LL f[20][10][10][10][10]; 9 const int n=8; 10 inline LL calc(int x1,int y1,int x2,int y2){ 11 int val=sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]; 12 return 1LL*val*val; 13 } 14 15 int main(){ 16 scanf("%d",&m); 17 for(int i=1;i<=n;i++) 18 for(int j=1;j<=n;j++) 19 scanf("%d",&a[i][j]); 20 for(int i=1;i<=n;i++) 21 for(int j=1;j<=n;j++) 22 sum[i][j]=sum[i][j-1]+a[i][j]; 23 for(int i=1;i<=n;i++) 24 for(int j=1;j<=n;j++) 25 sum[i][j]+=sum[i-1][j]; 26 for(int x1=1;x1<=n;x1++) 27 for(int y1=1;y1<=n;y1++) 28 for(int x2=x1;x2<=n;x2++) 29 for(int y2=y1;y2<=n;y2++) 30 f[1][x1][y1][x2][y2]=calc(x1,y1,x2,y2); 31 for(int i=2;i<=m;i++) 32 for(int x1=1;x1<=n;x1++) 33 for(int y1=1;y1<=n;y1++) 34 for(int x2=x1;x2<=n;x2++) 35 for(int y2=y1;y2<=n;y2++){ 36 LL tmp=inf; 37 for(int j=x1;j<x2;j++) 38 tmp=min(tmp,f[i-1][x1][y1][j][y2]+calc(j+1,y1,x2,y2)), 39 tmp=min(tmp,f[i-1][j+1][y1][x2][y2]+calc(x1,y1,j,y2)); 40 for(int j=y1;j<y2;j++) 41 tmp=min(tmp,f[i-1][x1][y1][x2][j]+calc(x1,j+1,x2,y2)), 42 tmp=min(tmp,f[i-1][x1][j+1][x2][y2]+calc(x1,y1,x2,j)); 43 f[i][x1][y1][x2][y2]=tmp; 44 } 45 printf("%lld\n",f[m][1][1][n][n]); 46 return 0; 47 }