title
POJ 1191
CH POJ1191
Description
将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差 ,其中平均值 ,xi为第i块矩形棋盘的总分。
请编程对给出的棋盘及n,求出O’的最小值。
Input
第1行为一个整数n(1 < n < 15)。
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
Output
仅一个数,为O’(四舍五入精确到小数点后三位)。
Sample Input
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
Sample Output
1.633
Source
analysis
NOI1999的dp都好厉害呀= =
一定要认真读题!
先看这个均方差的式子,可以进行化简:
显然
和
都是定值,那么我们的任务是求
的最小值。
设
表示切第i刀,剩余的矩形左上角和右下角的坐标是
和
,除了剩余部分其它部分的
平方和的最小值。那么
可以向
转移,只需要暴力枚举第
刀从哪里切了一刀即可。
矩阵前缀和搞的时候细节比较多,一定要细心。
wrong reason
在状态转移时,我竟然把 写成了 ,竟然还能过掉两个样例。。。。。。。。。。。抵制手残!!!!!!
code
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9+7;
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
int a[10][10],w[10][10];
double f[20][10][10][10][10];
double ans,sum;
inline double add(int x1,int y1,int x2,int y2)
{
double ww=w[x2][y2]-w[x1-1][y2]-w[x2][y1-1]+w[x1-1][y1-1]-sum;
return ww*ww;
}
int main()
{
int n;read(n);
sum=0;
for (int i=1; i<=8; ++i)
for (int j=1; j<=8; ++j)
read(a[i][j]),sum+=a[i][j];
sum=sum/n;
for (int i=1; i<=8; ++i)
for (int j=1; j<=8; ++j)
w[i][j]=w[i-1][j]+w[i][j-1]-w[i-1][j-1]+a[i][j];
for (int i=0; i<=n; ++i)
for (int x1=1; x1<=8; ++x1)
for (int y1=1; y1<=8; ++y1)
for (int x2=x1; x2<=8; ++x2)
for (int y2=y1; y2<=8; ++y2)
f[i][x1][y1][x2][y2]=inf;
f[0][1][1][8][8]=0;
for (int i=1; i<n; ++i)
for (int x1=1; x1<=8; ++x1)
for (int y1=1; y1<=8; ++y1)
for (int x2=x1; x2<=8; ++x2)
for (int y2=y1; y2<=8; ++y2)
for (int j=1; j<=8; ++j)
{
if (x1-j>0)
f[i][x1][y1][x2][y2]=min(f[i-1][x1-j][y1][x2][y2]+add(x1-j,y1,x1-1,y2),f[i][x1][y1][x2][y2]);
if (y1-j>0)
f[i][x1][y1][x2][y2]=min(f[i-1][x1][y1-j][x2][y2]+add(x1,y1-j,x2,y1-1),f[i][x1][y1][x2][y2]);
if (x2+j<=8)
f[i][x1][y1][x2][y2]=min(f[i-1][x1][y1][x2+j][y2]+add(x2+1,y1,x2+j,y2),f[i][x1][y1][x2][y2]);
if (y2+j<=8)
f[i][x1][y1][x2][y2]=min(f[i-1][x1][y1][x2][y2+j]+add(x1,y2+1,x2,y2+j),f[i][x1][y1][x2][y2]);
}
ans=inf;
for (int x1=1; x1<=8; ++x1)
for (int y1=1; y1<=8; ++y1)
for (int x2=x1; x2<=8; ++x2)
for (int y2=y1; y2<=8; ++y2)
if (f[n-1][x1][y1][x2][y2]+add(x1,y1,x2,y2)<ans)
ans=f[n-1][x1][y1][x2][y2]+add(x1,y1,x2,y2);
ans=ans/n;
ans=sqrt(ans);
printf("%.3f\n",ans);
return 0;
}