zoj 3614 二维RMQ

链接: 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;
}



你可能感兴趣的:(c,query)