现在的位置: 首页 > 综合 > 正文

zoj 4738 Choir 二维RMQ+预处理

2018年01月11日 ⁄ 综合 ⁄ 共 2959字 ⁄ 字号 评论关闭

题意:给定n*m的矩阵小格。每个小格都有一个数值,代表这个位置人的身高。给q个询问,每个询问给 x,y 要求在这个n*m的矩阵中找一个x*y的子矩阵(n是行m是列,x是行,y是列)这个子矩阵中除去最高的那个人。剩余人的方差。求满足x*y的子矩阵的方差最小的xy坐标,并输出方差 


首先贡献一发方差公式:


可以把它分拆成 

((x1*x1+x2*x2+.....+xn*xn) - 2*(x1+x2+..+xn)*(平均值)+n*(平均值)*(平均值))/n

想到这里的时候已经成功了一半了。所谓好的开端就是成功的一半。

现在我们转换一下问题:

我转换成了一下三个小问题:

1)求出给定的一个大矩形中的某个子矩形的和,我们希望复杂度尽量低

2)求出给定的一个大矩形中的某个子矩形的平方的和,我们希望复杂度尽量低

3)求出给定的一个大矩形中的某个子矩形的最大值,我们希望复杂度也尽量低

好了,如果能明白并写出这三个小问题的解。那么这题也就这样了。

1)可以预处理出左上角到当前i,j位置的和sum1[i][j],这样查询的时候就可以直接O(1)的查询了,对于给定的x,y,x1,y1这个矩形。那么这个矩形的值为:

sum1[x1][y1]-sum1[x-1][y1]-sum1[x1][y1-1]-sum1[x-1][y-1]

2)可以预处理出左上角到当前i,j位置的平方的和,这样查询的时候也可以直接O(1)的查询,同上。1)和2)是同一个做法。

3)2维RMQ求子矩形的最大值。

好了,代码:

//author: CHC
//First Edit Time:	2014-07-23 15:00
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
using namespace std;
#define MAXN 310
int rec[MAXN][MAXN];
int dp[MAXN][MAXN][10][10];
int n,m;
int summ[MAXN][MAXN];
int sumn[MAXN][MAXN];
int sum1[MAXN][MAXN];
int sum2[MAXN][MAXN];
inline int maxm(int a,int b,int c,int d){
    if(a<b)a=b; if(a<c)a=c; if(a<d)a=d;
    return a;
}
void st()
{
    for(int k=0;(1<<k)<=n;k++)
    for(int l=0;(1<<l)<=m;l++)
    for(int i=1;i+(1<<k)-1<=n;i++)
    for(int j=1;j+(1<<l)-1<=m;j++)
    {
        if(!k&&!l){
            dp[i][j][k][l]=rec[i][j];
        }
        else if(k==0){
            dp[i][j][k][l]=max(dp[i][j][k][l-1],dp[i][j+(1<<(l-1))][k][l-1]);
        }
        else if(l==0){
            dp[i][j][k][l]=max(dp[i][j][k-1][l],dp[i+(1<<(k-1))][j][k-1][l]);
        }
        else {
        dp[i][j][k][l]=maxm(dp[i][j][k-1][l-1],dp[i+(1<<(k-1))][j][k-1][l-1],
                            dp[i][j+(1<<(l-1))][k-1][l-1],dp[i+(1<<(k-1))][j+(1<<(l-1))][k-1][l-1]);
        }
    }
}
int rmq2dmax(int x,int y,int x1,int y1){
    int k=log(x1-x+1)/log(2);
    int l=log(y1-y+1)/log(2);
    return maxm(dp[x][y][k][l],dp[x1-(1<<k)+1][y][k][l],
                dp[x][y1-(1<<l)+1][k][l],dp[x1-(1<<k)+1][y1-(1<<l)+1][k][l]);
}
void pre_do(){
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++){
        summ[i][j]=summ[i][j-1]+rec[i][j];
        sum1[i][j]=sum1[i-1][j]+summ[i][j];
        sumn[i][j]=sumn[i][j-1]+rec[i][j]*rec[i][j];
        sum2[i][j]=sum2[i-1][j]+sumn[i][j];
    }
}
int getare1(int x,int y,int x1,int y1){
    return sum1[x1][y1]-sum1[x-1][y1]-sum1[x1][y-1]+sum1[x-1][y-1];
}
int getare2(int x,int y,int x1,int y1){
    return sum2[x1][y1]-sum2[x-1][y1]-sum2[x1][y-1]+sum2[x-1][y-1];
}
int main()
{
    memset(summ,0,sizeof(summ));
    memset(sum1,0,sizeof(sum1));
    int cas=0;
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&rec[i][j]);
        st();
        pre_do();
        int q;
        scanf("%d",&q);
        printf("Case %d:\n",++cas);
        int nn,mm;
        while(q--){
            scanf("%d%d",&nn,&mm);
            int posx=1,posy=1;
            double fancha=-1.0;
            for(int x=1;x+nn-1<=n;x++)
            for(int y=1;y+mm-1<=m;y++){
                int ma=rmq2dmax(x,y,x+nn-1,y+mm-1);
                int a1=getare1(x,y,x+nn-1,y+mm-1)-ma;
                int a2=getare2(x,y,x+nn-1,y+mm-1)-ma*ma;
                double _x=(double)a1/(nn*mm-1);
                double val=1.0*(a2-2*_x*a1+(nn*mm-1)*_x*_x)/(nn*mm-1);
                if(fancha<0||fancha>val){
                    fancha=val;
                    posx=x;posy=y;
                }
                //printf("%lf\n",a2-2*_x*a1+(nn*mm-1)*_x*_x);
                //printf("%d %lf %lf\n",a2,2*_x*a1,(nn*mm-1)*_x*_x);
                //printf("%d %d ma:%d a1:%d a2:%d val:%lf _x:%lf\n",x,y,ma,a1,a2,val,_x);
            }
            if(fancha<0)fancha=0.0;
            printf("(%d, %d), %.2lf\n",posx,posy,fancha);
        }
    }
    return 0;
}

抱歉!评论已关闭.