1048: [HAOI2007]分割矩阵
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 498 Solved: 362
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块矩形棋盘。(每次切割都只能沿着棋盘格子的边进行)
原棋盘上每一格有一个分值,一块矩形棋盘的总分为其所含各格分值之和。现在需要把棋盘按上述规则分割成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
和这道题超级像。。
但是如果你仔细观察会发现不同。
poj这道题切完一刀之后,只能对其中一块进行分割!!而bzoj这道可以对两块进行分割!!
因此poj这道不需要枚举分割的块数。其中一个是1,另一个是k-1!!