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 }

 

posted @ 2021-04-05 12:01  acmloser  阅读(71)  评论(0编辑  收藏  举报