题意:给定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;
}