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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章