链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4738
题意:给你一个 n * m 的矩阵,然后有Q次询问,求一个a * b 的 子矩阵,使其方差最小(去掉子矩阵的最大值以后)
分析:n 和 m 的范围是 0 - 300,q 最大为100,枚举的话是10 ^ 6,这就要求我们在O(1) - O(n^2) 内求出子矩阵的方差,我们可以将方差拆开,预处理下需要的数据,然后可以在 O(1)的时间内求出方差,其中方差拆开如下 方差 = (矩阵平方和 + n × 平均值 × 平均值 - 2 × 平均值 × 矩阵和) / n(n为矩阵内的元素个数) ,求的过程中减去最大值,rmq预处理之后可以在0(1)内求出矩阵最值。
code:
#include<stdio.h> #include<string.h> #include<math.h> #define max(a,b) ((a)>(b)?(a):(b)) #define INF (double) 1e30 const int maxn = 301; int mat[maxn][maxn],r,c,q,dp[maxn][maxn][9][9];; double sum[maxn][maxn],sum_2[maxn][maxn],lie[maxn][maxn],lie_2[maxn][maxn]; int query_2d(int x1,int x2,int y1,int y2); void init(); void solve(int a,int b); void RMQ_2D(); int main() { int cas = 1; while(scanf("%d%d",&r,&c) != EOF) { for(int i = 1; i <= r; i ++) for(int j = 1; j <= c; j ++) scanf("%d",&mat[i][j]); printf("Case %d:\n",cas ++); init(); RMQ_2D(); scanf("%d",&q); while(q --) { int a,b; scanf("%d%d",&a,&b); solve(a - 1,b - 1); } } return 0; } void init() { int i,j; for(int i = 1; i <= r; i ++)//init lie for(int j = 1; j <= c; j ++) { lie[i][j] = lie[i - 1][j] + mat[i][j]; lie_2[i][j] = lie_2[i - 1][j] + mat[i][j] * mat[i][j]; } for(int i = 1; i <= r; i ++) for(int j = 1; j <= c; j ++) { sum[i][j] = sum[i][j - 1] + lie[i][j]; sum_2[i][j] = sum_2[i][j - 1] + lie_2[i][j]; } } void solve(int a,int b) { int ans_x,ans_y; double ans = INF; for(int i = 1;i + a <= r;i ++) { for(int j = 1;j + b <= c;j ++) { int n = (a + 1) * (b + 1) - 1; int rq = query_2d(i,i + a,j,j + b); double tp = sum_2[i + a][j + b] - sum_2[i + a][j - 1] - sum_2[i - 1][j + b] + sum_2[i - 1][j - 1] - rq * rq;//平方和 double tv = sum[i + a][j + b] - sum[i + a][j - 1] - sum[i - 1][j + b] + sum[i - 1][j - 1] - rq;//和 double ae = tv / n; double here = (tp + n * ae * ae - 2 * ae * tv) / n; if(here < ans) { ans_x = i; ans_y = j; ans = here; } } } printf("(%d, %d), %.2f\n",ans_x,ans_y,ans); } void RMQ_2D() { for(int i = 1; i <= r; i ++) for(int j = 1; j <= c; j ++) dp[i][j][0][0] = mat[i][j]; int t = (int) (log((double)c) / log(2.0)); for(int i = 0; i <= t; i ++) { for(int j = 0; j <= t; j ++) { if(i == 0 && j == 0) continue; for(int row = 1; row + (1 << i) - 1 <= r; row ++) { for(int col = 1; col + (1 << j) - 1 <= c; col ++) { if(i == 0) dp[row][col][i][j] = max(dp[row][col][i][j - 1] , dp[row][col + (1 << (j - 1))][i][j - 1]); else dp[row][col][i][j] = max(dp[row][col][i - 1][j],dp[row + (1 << (i - 1))][col][i - 1][j]); } } } } } int query_2d(int x1,int x2,int y1,int y2) { int kx = log(double(x2 - x1 + 1)) / log(2.0); int ky = log(double(y2 - y1 + 1)) / log(2.0); int m1 = dp[x1][y1][kx][ky]; int m2 = dp[x2 - (1 << kx) + 1][y1][kx][ky]; int m3 = dp[x1][y2 - (1 << ky) + 1][kx][ky]; int m4 = dp[x2 - (1 << kx) + 1][y2 - (1 << ky) + 1][kx][ky]; int ans = max(max(m1,m2),max(m3,m4)); return ans; }