1048: [HAOI2007]分割矩阵
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 498 Solved: 362
[ Submit][ Status]
Description
将一个a*b的数字矩阵进行如下分割:将原矩阵沿某一条直线分割成两个矩阵,再将生成的两个矩阵继续如此分割(当然也可以只分割其中的一个),这样分割了(n-1)次后,原矩阵被分割成了n个矩阵。(每次分割都只能沿着数字间的缝隙进行)原矩阵中每一位置上有一个分值,一个矩阵的总分为其所含各位置上分值之和。现在需要把矩阵按上述规则分割成n个矩阵,并使各矩阵总分的均方差最小。请编程对给出的矩阵及n,求出均方差的最小值。
Input
第一行为3个整数,表示a,b,n(1
Output
仅一个数,为均方差的最小值(四舍五入精确到小数点后2位)
Sample Input
2 3 4 6
5 7 5 1
10 4 0 5
2 0 2 3
4 1 1 1
Sample Output
为了方便计算先把求平均方差的公式展开化简,无论怎样分割平均数都不变。
原来的式子就变成了(x1^2+x2^2+...+xn^2)/n-(tot/n)^2
那么就是要让x的平方之和最小。
f[x1][y1][x2][y2][k]表示以(x1,y1)为左上角,(x2,y2)为右下角,把这个矩阵分成k份的最小平方和。
那么我们枚举切割位置(横着、竖着),再枚举切割后的两块中一块分成的块数(1~k-1),用记忆化搜索来做就可以了。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int f[15][15][15][15][15],n,pre[15][15],x[15][15],a,b;
bool ok(int x1,int y1,int x2,int y2,int k)
{
if ((x2-x1+1)*(y2-y1+1)>=k) return true;
return false;
}
int cal(int x1,int y1,int x2,int y2)
{
return (pre[x2][y2]-pre[x1-1][y2]-pre[x2][y1-1]+pre[x1-1][y1-1]);
}
int dp(int x1,int y1,int x2,int y2,int k)
{
if (f[x1][y1][x2][y2][k]!=-1) return f[x1][y1][x2][y2][k];
if (k==1)
{
f[x1][y1][x2][y2][k]=cal(x1,y1,x2,y2)*cal(x1,y1,x2,y2);
return f[x1][y1][x2][y2][k];
}
int minn=inf;
//vertical
for (int i=y1+1;i<=y2;i++)
for (int j=1;j<k;j++)
{
int ma=max(j,k-j),mi=min(j,k-j);
if (ok(x1,y1,x2,i-1,ma))
minn=min(minn,dp(x1,y1,x2,i-1,ma)+dp(x1,i,x2,y2,mi));
if (ok(x1,i,x2,y2,ma))
minn=min(minn,dp(x1,y1,x2,i-1,mi)+dp(x1,i,x2,y2,ma));
}
//horizontal
for (int i=x1+1;i<=x2;i++)
for (int j=1;j<k;j++)
{
int ma=max(j,k-j),mi=min(j,k-j);
if (ok(x1,y1,i-1,y2,ma))
minn=min(minn,dp(x1,y1,i-1,y2,ma)+dp(i,y1,x2,y2,mi));
if (ok(i,y1,x2,y2,ma))
minn=min(minn,dp(i,y1,x2,y2,ma)+dp(x1,y1,i-1,y2,mi));
}
return f[x1][y1][x2][y2][k]=minn;
}
int main()
{
scanf("%d%d%d",&a,&b,&n);
for (int i=1;i<=a;i++)
for (int j=1;j<=b;j++)
scanf("%d",&x[i][j]),pre[i][j]=pre[i][j-1]+x[i][j];
for (int i=2;i<=a;i++)
for (int j=1;j<=b;j++)
pre[i][j]+=pre[i-1][j];
memset(f,-1,sizeof(f));
dp(1,1,a,b,n);
double ans=0.0;
ans=(double)f[1][1][a][b][n]/(double)n-(double)(pre[a][b]*pre[a][b])/(double)(n*n);
printf("%.2lf\n",sqrt(ans));
return 0;
}
其实这道题一开始wa了无数次。。
我曾经做过poj1191
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 12647 | Accepted: 4481 |
Description

原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。
均方差


请编程对给出的棋盘及n,求出O'的最小值。
Input
第2行至第9行每行为8个小于100的非负整数,表示棋盘上相应格子的分值。每行相邻两数之间用一个空格分隔。
Output
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
和这道题超级像。。
但是如果你仔细观察会发现不同。
poj这道题切完一刀之后,只能对其中一块进行分割!!而bzoj这道可以对两块进行分割!!
因此poj这道不需要枚举分割的块数。其中一个是1,另一个是k-1!!