poj 1191 棋盘分割 公式转换,横纵方向动态规划
将方差公式转换 :
方差: 均值:
得到:
我们知道 均值 X 为定值,与如何划分无关, 所以上述公式, 仅与 有关.
那么我们可以通过求出最优的 , 即可求出最优 方差值.
因为对于任意矩形,我们可以通过 其左上角,右下角 坐标来唯一确定,且要保证结果无后效性,我们增加一维切割次数N
定义状态 DP(N,X1,Y1,X2,Y2) 为将 矩形(X1,Y1,X2,Y2)划分N次 的最小 平方值和
则根据题目得到
对于当前状态 DP(K,X1,Y1,X2,Y2)
对于 矩形 (X1,Y1,X2,Y2) ,依据题目要求只可在边缘切割,所以我们的切割方案分为两类:
一,沿着 横坐标 切割,假定切割值为a,则划分为两个小矩形:S1(X1,Y1,A,Y2),S2(A+1,Y1,X2,Y2) //这里是按点划分,而非按块,A点属于前者,则A+1属于后者
二,沿着 纵坐标 切割,假定切割值为B,则划分为两个小矩形:S1(X1,Y1,X2,B),S2(X1,B+1,X2,Y2)
两种情形下的划分后,我们可以从中任选一块继续划分
所以状态转移方程为:
DP(K,X1,Y1,X2,Y2)= MIN
{
MIN( DP(K-1,X1,Y1,A,Y2)+S(A+1,Y1,X2,Y2), DP(K-1,A+1,Y1,X2,Y2)+S(X1,Y1,A,Y2) ), // X1 <= A < X2
MIN( DP(K-1,X1,Y1,X2,B)+S(X,B+1,X2,Y2),DP(K-1,X1,B+1,X2,Y2)+S(X1,Y1,X2,B)) // Y1 <= B < Y2
}
这里的 S(X1,Y1,X2,Y2)指此矩形的权值和的平方
我们可以通过 O(N*N)预处理出 以(1,1)为左上角顶点的矩形权值和,然后通过简单容斥(S(X2,Y2)-S(X1-1,Y2)-S(X1,Y2-1)+S(X1-1,Y1-1) )
来 O(1)实现策略。
总时间复杂度为 O(M^5*N)
解题代码:
#include<stdio.h> #include<string.h> #include<math.h> const int inf = 0x3fffffff; int MIN( int a, int b ){ return a < b ? a : b; } int mp[10][10], s[10][10], dp[15][10][10][10][10]; int n; int comp( int x1, int y1, int x2, int y2 ) { // return the area of (x1,y1,x2,y2) int tmp = s[x2][y2]-s[x2][y1-1]-s[x1-1][y2] + s[x1-1][y1-1]; return (tmp*tmp); } int dfs( int k, int x1, int y1, int x2, int y2 ) { if( dp[k][x1][y1][x2][y2] != -1 ) return dp[k][x1][y1][x2][y2]; if( k == 0 ){ int tmp = comp(x1,y1,x2,y2); return (dp[k][x1][y1][x2][y2]=tmp); } int res = inf; // solve row for(int a = x1; a < x2; a++) { int tmp = MIN( dfs(k-1,x1,y1,a,y2)+comp(a+1,y1,x2,y2), dfs(k-1,a+1,y1,x2,y2)+comp(x1,y1,a,y2) ); res = MIN( res, tmp ); } // solve colomn for(int b = y1; b < y2; b++) { int tmp = MIN( dfs(k-1,x1,y1,x2,b)+comp(x1,b+1,x2,y2), dfs(k-1,x1,b+1,x2,y2)+comp(x1,y1,x2,b) ); res = MIN( res, tmp ); } return (dp[k][x1][y1][x2][y2]=res); } int main() { while( scanf("%d", &n) != EOF) { int sum = 0; for(int i = 1; i <= 8; i++) for(int j = 1; j <= 8; j++) { scanf("%d", &mp[i][j] ); sum += mp[i][j]; } memset(s,0,sizeof(s)); memset(dp,0xff,sizeof(dp)); // Get the Area S(1,1,x,y) for(int r = 1; r <= 8; r++) { s[1][r] = s[1][r-1] + mp[1][r]; s[r][1] = s[r-1][1] + mp[r][1]; } for(int r = 2; r <= 8; r++) for(int c = 2; c <= 8; c++) s[r][c] = s[r-1][c]+s[r][c-1]-s[r-1][c-1] + mp[r][c]; int x = dfs( n-1, 1, 1, 8, 8 ); // printf("x = %d\n", x ); double ans = sqrt( 1.*((x*n)-sum*sum)/(n*n) ) ; printf("%.3lf\n", ans ); } return 0; }