棋盘分割
描述
将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 ,xi为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。
输入
第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
输出
仅一个数,为O'(四舍五入精确到小数点后三位)。
样例输入
样例输出
来源
Noi 99
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 ,xi为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O'的最小值。
第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
1.633
题解:dp
我们其实可以将均方差公式化简。
sqrt((x1^2+x2^2+...+xn^2-(x1+x2+...+xn)^2/n)/n)
在这个式子中只有x1^2+x2^2+...+xn^2是变量,其他的都是常量。我们要使均方差最小,其实就是最小化x1^2+x2^2+...+xn^2的值。
因为每次只能切割下一个矩形,并且要求剩下的部分仍然是矩形。那么每次就只有上下左右四种切割方式。然后考虑进行记忆化搜索,dfs(x,y,x1,y1,n) (x,y)表示剩下矩形的左上角坐标 (x1,y1) 剩下矩形的右下角坐标 n表示还需要切割的次数。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 20
#define inf 1000000000
using namespace std;
int n,f[N][N][N][N][N],a[N][N],num;
int sum[N][N];
int calc(int x)
{
return x*x;
}
int dfs(int x,int y,int x1,int y1,int n)
{
if (x>x1||y>y1) return inf;
if (n==1) {
f[x][y][x1][y1][n]=calc(sum[x1][y1]-sum[x-1][y1]-sum[x1][y-1]+sum[x-1][y-1]);
return f[x][y][x1][y1][n];
}
if (f[x][y][x1][y1][n]!=-1) return f[x][y][x1][y1][n];
int ans=inf;
for (int i=x;i<=x1;i++)
{
int t=dfs(i+1,y,x1,y1,n-1)+calc(sum[i][y1]-sum[i][y-1]-sum[x-1][y1]+sum[x-1][y-1]);
int t1=dfs(x,y,i,y1,n-1)+calc(sum[x1][y1]-sum[i][y1]-sum[x1][y-1]+sum[i][y-1]);
ans=min(t,ans); ans=min(t1,ans);
}
for (int i=y;i<=y1;i++)
{
int t=dfs(x,i+1,x1,y1,n-1)+calc(sum[x1][i]-sum[x-1][i]-sum[x1][y-1]+sum[x-1][y-1]);
int t1=dfs(x,y,x1,i,n-1)+calc(sum[x1][y1]-sum[x1][i]-sum[x-1][y1]+sum[x-1][i]);
ans=min(t,ans); ans=min(t1,ans);
}
f[x][y][x1][y1][n]=ans;
return f[x][y][x1][y1][n];
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d",&n);
for (int i=1;i<=8;i++)
for (int j=1;j<=8;j++)
scanf("%d",&a[i][j]),num+=a[i][j];
for (int i=1;i<=8;i++)
for (int j=1;j<=8;j++)
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
memset(f,-1,sizeof(f));
int ans=dfs(1,1,8,8,n);
double ans1=(double)ans-(double)calc(num)/(double)n;
ans1/=(double)n;
printf("%.3lf\n",sqrt(ans1));
}