321. 棋盘分割
题目链接
321. 棋盘分割
将一个 \(8×8\) 的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了 \((n−1)\) 次后,连同最后剩下的矩形棋盘共有 \(n\) 块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。
现在需要把棋盘按上述规则分割成 \(n\) 块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差,其中平均值,\(x_i\) 为第 i 块矩形棋盘的总分。
请编程对给出的棋盘及 \(n\),求出均方差的最小值。
输入格式
第 \(1\) 行为一个整数 \(n\)。
第 \(2\) 行至第 \(9\) 行每行为 \(8\) 个小于 \(100\) 的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
输出格式
输出最小均方差值(四舍五入精确到小数点后三位)。
数据范围
\(1<n<15\)
输入样例:
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
输出样例:
1.633
解题思路
区间dp
二维平面上的区间dp:
-
状态表示:\(f[x_1][y_1][x_2][y_2][k]\) 表示左上角为 \((x_1,y_1)\),右上角为 \((x_2,y_2)\) 的子矩阵含有 \(k\) 个小矩阵的最小 \(\frac{\sum(x_i-X)^2}{n}\),其中 \(X\) 为整个矩阵分为 \(n\) 块的平均值
-
状态计算,令 \(t=f[x_1][y_1][x_2][y_2][k]\),其中 \(get\) 函数获取矩阵面积:
-
横向切:
-
- \(t=min(t,dp(i+1,y1,x2,y2,k-1)+get(x1,y1,i,y2))\)
-
- \(t=min(t,dp(x1,y1,i,y2,k-1)+get(i+1,y1,x2,y2))\)
-
纵向切:
-
- \(t=min(t,dp(x1,i+1,x2,y2,k-1)+get(x1,y1,x2,i))\)
-
- \(t=min(t,dp(x1,y1,x2,i,k-1)+get(x1,i+1,x2,y2))\)
-
时间复杂度:\((n\times 8^5)\)
代码
// Problem: 棋盘分割
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/323/
// Memory Limit: 10 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=16,M=9;
int n;
double f[M][M][M][M][N],s[M][M],X=0;
double get(int x1,int y1,int x2,int y2)
{
double sum=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];
sum-=X;
return sum*sum/n;
}
double dp(int x1,int y1,int x2,int y2,int k)
{
double &t=f[x1][y1][x2][y2][k];
if(t>=0)return t;
if(k==1)return t=get(x1,y1,x2,y2);
t=2e9;
for(int i=x1;i<x2;i++)
{
t=min(t,dp(i+1,y1,x2,y2,k-1)+get(x1,y1,i,y2));
t=min(t,dp(x1,y1,i,y2,k-1)+get(i+1,y1,x2,y2));
}
for(int i=y1;i<y2;i++)
{
t=min(t,dp(x1,i+1,x2,y2,k-1)+get(x1,y1,x2,i));
t=min(t,dp(x1,y1,x2,i,k-1)+get(x1,i+1,x2,y2));
}
return t;
}
int main()
{
cin>>n;
for(int i=1;i<M;i++)
for(int j=1;j<M;j++)
{
cin>>s[i][j];
X+=s[i][j];
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
X/=n;
memset(f,-1,sizeof f);
printf("%.3lf",sqrt(dp(1,1,8,8,n)));
return 0;
}