【BZOJ 1048】 [HAOI2007]分割矩阵

1048: [HAOI2007]分割矩阵

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 498   Solved: 362
[ Submit][ Status]

Description

将一个a*b的数字矩阵进行如下分割:将原矩阵沿某一条直线分割成两个矩阵,再将生成的两个矩阵继续如此分割(当然也可以只分割其中的一个),这样分割了(n-1)次后,原矩阵被分割成了n个矩阵。(每次分割都只能沿着数字间的缝隙进行)原矩阵中每一位置上有一个分值,一个矩阵的总分为其所含各位置上分值之和。现在需要把矩阵按上述规则分割成n个矩阵,并使各矩阵总分的均方差最小。请编程对给出的矩阵及n,求出均方差的最小值。

Input

第一行为3个整数,表示a,b,n(1

Output

仅一个数,为均方差的最小值(四舍五入精确到小数点后2位)

Sample Input

5 4 4
2 3 4 6
5 7 5 1
10 4 0 5
2 0 2 3
4 1 1 1

Sample Output

0.50


为了方便计算先把求平均方差的公式展开化简,无论怎样分割平均数都不变。


原来的式子就变成了(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

将一个8*8的棋盘进行如下分割:将原棋盘割下一块矩形棋盘并使剩下部分也是矩形,再将剩下的部分继续如此分割,这样割了(n-1)次后,连同最后剩下的矩形棋盘共有n块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行) 
【BZOJ 1048】 [HAOI2007]分割矩阵_第1张图片
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成n块矩形棋盘,并使各矩形棋盘总分的均方差最小。 
均方差 ,其中平均值 ,x i为第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

和这道题超级像。。


但是如果你仔细观察会发现不同。


poj这道题切完一刀之后,只能对其中一块进行分割!!而bzoj这道可以对两块进行分割!!


因此poj这道不需要枚举分割的块数。其中一个是1,另一个是k-1!!

你可能感兴趣的:(dp,OI,bzoj)