P5752 [NOI1999] 棋盘分割
考察:区间dp
思路:
这道题和凸边形的划分很像,枚举中间点,两边成为独立的两部分.只不过这题可以横,纵着切,所以是两个方向的区间dp.对于一块矩形板,我们选了上面,下面与平均值的标准差就确定了,快速求解矩阵和可用二维前缀和预处理.因为要用到二维,所以是五维转移方程f[x][y][a][b][k]表示(x,y)->(a,b)矩形内部还有k个子矩阵要选的最小标准差.用记忆化搜索实现比较省行.这里的标准差公式进行了化简.
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 using namespace std; 6 const int N = 16,M = 9; 7 const double INF = 99999999999999999; 8 int sum[M][M],n; 9 double f[M][M][M][M][N],avg; 10 double get(int x1,int y1,int x2,int y2) 11 { 12 int now = sum[x2][y2]-sum[x2][y1-1]-sum[x1-1][y2]+sum[x1-1][y1-1]; 13 return 1.0*now*now/n; 14 } 15 double dfs(int x1,int y1,int x2,int y2,int k) 16 { 17 double& v = f[x1][y1][x2][y2][k]; 18 if(v>=0) return v; 19 if(k==1) return get(x1,y1,x2,y2); 20 v = INF; 21 for(int i=x1;i<x2;i++) 22 { 23 double l = dfs(x1,y1,i,y2,k-1)+get(i+1,y1,x2,y2);//选择上面 24 double r = dfs(i+1,y1,x2,y2,k-1)+get(x1,y1,i,y2); 25 v = min(l,v); 26 v = min(r,v); 27 } 28 for(int j=y1;j<y2;j++) 29 { 30 double l = dfs(x1,y1,x2,j,k-1)+get(x1,j+1,x2,y2); 31 double r = dfs(x1,j+1,x2,y2,k-1)+get(x1,y1,x2,j); 32 v = min(l,v); 33 v = min(r,v); 34 } 35 return v; 36 } 37 int main() 38 { 39 scanf("%d",&n); 40 for(int i=1;i<=8;i++) 41 for(int j=1;j<=8;j++) 42 { 43 scanf("%d",&sum[i][j]); 44 sum[i][j] += sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]; 45 } 46 avg = sum[8][8]*1.0/n; 47 memset(f,-1,sizeof f); 48 double ans = dfs(1,1,8,8,n); 49 printf("%.3lf\n",sqrt(ans-avg*avg)); 50 return 0; 51 }