棋盘切割 DP POJ 1191

把方差公式先变形为 σ2 = (1/n)∑xi2-xa2

xa为平均值。

由于要求标准差最小,只需方差最小,平均值都是一样的,n也是一样的,这样原问题就变为求这n快小棋盘总分的平方和最小

考虑左上角为(x1,y1),右上角为(x2,y2)的棋盘,设该棋盘切割K次后得到的K+1块矩形的总分平方和最小值为d[k,x1,y1,x2,y2]。该棋盘的总分平方和为

s[x1,y1,x2,y2].则它可以沿着横线切,也可以沿着竖线切,然后选一块继续切(这里可以用递归完成)

状态转移方程为d[k,x1,y1,x2,y2] =  min{

min{ d[k-1,x1,y1,a,y2] + s[a+1,y1,x2,y2] , d[k-1,a+1,y1,x2,y2] + s[x1,y1,a,y2] },    (x1 <= a < x2)

min{ d[k-1,x1,y1,x2,b] + s[x1,b+1,x2,y2] , d[k-1,x1,b+1,x2,y2] + s[x1,y1,x2,b] }  (y1 <= b < y2)

}

贴代码:

View Code
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cmath>
 4 #define N 9
 5 #define min(a,b) a<b?a:b
 6 #define INF 100000000
 7 int n;
 8 int map[N][N];
 9 int presum[N][N];
10 int d[16][N][N][N][N];//用d[k][x1][y1][x2][y2]表示左上角为(x1,y1)
11 //右下角为(x2,y2)的棋盘切成了k+1块时最小的总分平方和值
12 void presolve()
13 {
14     //预处理,算出所有左上角为(1,1)的所有矩阵元素和
15     int i,j;
16     for(i=0; i<=N; ++i)
17         presum[0][i] = 0,presum[i][0] = 0;
18     for(i=1; i<N; ++i)
19     {
20         int rowsum = 0;
21         for(j=1; j<N; ++j)
22         {
23             scanf("%d",&map[i][j]);
24             rowsum += map[i][j];
25             presum[i][j] = presum[i-1][j] + rowsum;
26         }
27     }
28 }
29 int rectSquare(int x1,int y1,int x2,int y2)
30 {
31     //计算左上角为(x1,y1),右下角为(x2,y2)的棋盘的总分平方和
32     int ret = presum[x2][y2] -presum[x1-1][y2] -presum[x2][y1-1] +presum[x1-1][y1-1];
33     return ret*ret;
34 }
35 //DP程序
36 int DP(int k,int x1,int y1,int x2,int y2)
37 {
38     int t,c,e;
39     int MIN = INF;//用来求最后的d[k][x1][y1][x2][y2]
40     if(d[k][x1][y1][x2][y2] != -1)//记忆化搜索???已经求过了,不再求,直接用
41         return d[k][x1][y1][x2][y2];
42     if(k == 0)//切0刀,不就是该棋盘了吗?最小值也是该棋盘的总分平方和,边界条件
43         return d[k][x1][y1][x2][y2]=rectSquare(x1,y1,x2,y2);
44     for(int a = x1; a < x2; ++a)
45     {
46         //横着切成了两块
47         c = rectSquare(a+1,y1,x2,y2);
48         e = rectSquare(x1,y1,a,y2);
49         t = min(DP(k-1,x1,y1,a,y2) + c,DP(k-1,a+1,y1,x2,y2) + e);//选取一块继续切割
50         MIN = min(MIN,t);
51     }
52     for(int b = y1; b < y2; ++b)
53     {
54         //竖着切成了两块
55         c = rectSquare(x1,b+1,x2,y2);
56         e = rectSquare(x1,y1,x2,b);
57         t= min(DP(k-1,x1,y1,x2,b)+c,DP(k-1,x1,b+1,x2,y2)+e);//选取一块继续切割
58         MIN = min(MIN,t);
59     }
60     d[k][x1][y1][x2][y2] = MIN;//所有的情况都考虑完后,所得就是该最小值
61 //    printf("d[%d][%d][%d][%d][%d] = %d\n",k,x1,y1,x2,y2,d[k][x1][y1][x2][y2]);
62     return MIN;
63 }
64 int main()
65 {
66 //    freopen("in.cpp","r",stdin);
67     scanf("%d",&n);
68     presolve();
69     memset(d,-1,sizeof(d));
70     int sumsquare = DP(n-1,1,1,8,8);
71     double f = n*sumsquare - presum[8][8]*presum[8][8];
72     f = sqrt(f)/n;
73     printf("%.3f\n",f);
74 //    printf("%d\n",0x7fffffff);
75     return 0;
76 }
注意:
用double,然后用%.3lf WA 用%.3fAC 在DP时min的初始值赋为10^7WA 赋为10^8AC 这点我还比较想得通,棋盘最大的总分和为6400,最大的平方和即为6400^6400,为40960000 你赋的min值至少应该大于这个数,否则就不对了,所以10^7可能会错 我中间赋过一个0x7fffffff,竟然导致出现了负数,怎么会啊。。。。。不了解
补充:我现在理解了为什么出现负数,因为0x7fffffff是最大的整数数了,再加就溢出了,成为负的了·····

posted on 2013-05-05 16:24  allh123  阅读(171)  评论(0编辑  收藏  举报

导航