传送门:棋盘分割
思路:状态表示:f[x1][y1][x2][y2][k]表示将矩阵分割成k部分的方案的方差的最小值
状态计算:按照横着砍和竖着砍的不同方式,枚举每次砍出来的两个矩形,将其中一个继续 砍下去,结果就是最小的那个。
代码:
#include<iostream>
#include<cstring>
#include <cmath>
#include<algorithm>
using namespace std;
const int N = 15, M = 9;
const double INF = 1e9;
int n, m = 8;
int s[M][M];
double f[M][M][M][M][N];
double X;
int get_sum(int x1, int y1, int x2, int y2)
{
return s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
}
double get(int x1, int y1, int x2, int y2)
{
double sum = get_sum(x1, y1, x2, y2) - X;
return (double)sum * sum / n;
}
double dp(int x1, int y1, int x2, int y2, int k)
{
double &v = f[x1][y1][x2][y2][k];
if (v >= 0) return v;
if (k == 1) return v = get(x1, y1, x2, y2);
v = INF;
for (int i = x1; i < x2; i ++ )
{
v = min(v,get(x1,y1,i,y2)+dp(i+1,y1,x2,y2,k-1));
v = min(v,get(i+1,y1,x2,y2)+dp(x1,y1,i,y2,k-1));
}
for (int i = y1; i < y2; i ++ )
{
v = min(v,get(x1,y1,x2,i)+dp(x1,i+1,x2,y2,k - 1));
v = min(v,get(x1,i+1,x2,y2)+dp(x1,y1,x2,i,k - 1));
}
return v;
}
int main()
{
cin >> n;
for (int i = 1; i <= m; i ++ )
for (int j = 1; j <= m; j ++ )
{
cin >> s[i][j];
s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
}
X = (double)s[m][m] / n;
memset(f, -1, sizeof f);
printf("%.3lf\n", sqrt(dp(1, 1, 8, 8, n)));
return 0;
}