ZOJ 3614 Choir(二维RMQ)

5 篇文章 0 订阅

首先把矩阵和和矩阵平方和算出来,然后枚举每一个位置,中间计算的时候要减去最大值,这里就用到了二维RMQ O(1)的时间

写的时候用int溢出,找了好久.

 方差 = (矩阵平方和 + n × 平均值 × 平均值 - 2 × 平均值 × 矩阵和) / n(n为矩阵内的元素个数)

#include <iostream>
#include <memory.h>
#include <cstdio>
#include <cmath>
using namespace std;
const int maxn=301;
#define mmax(a,b) ((a)>(b)?(a):(b))
int dp[maxn][maxn][9][9],grid[maxn][maxn],N,M,Q;
double t2[maxn][maxn],qSum[maxn][maxn],t1[maxn][maxn],sum[maxn][maxn];
void init(){
	for (int i=1;i<=N;++i){
		for (int j=1;j<=M;++j){
			t1[i][j]=t1[i-1][j]+grid[i][j];
			t2[i][j]=t2[i-1][j]+grid[i][j]*grid[i][j];
		}
	}
	for (int i=1;i<=N;++i){
		for (int j=1;j<=M;++j){
			sum[i][j]=sum[i][j-1]+t1[i][j];
			qSum[i][j]=qSum[i][j-1]+t2[i][j];
		}
	}
	for (int i=1;i<=N;++i){
		for (int j=1;j<=M;++j){
			dp[i][j][0][0]=grid[i][j];
		}
	}
	int km=int(log((double)M)/log(2.0));
	for (int i=0;i<=km;++i){	
		for (int j=0;j<=km;++j){
			if(i==0&&j==0)continue;
			for (int ii=1;ii+(1<<i)-1<=N;++ii){
				for (int jj=1;jj+(1<<j)-1<=M;++jj){
					if(i==0){
						dp[ii][jj][i][j]=mmax(dp[ii][jj][i][j-1],dp[ii][jj+(1<<(j-1))][i][j-1]);
					}else{
						dp[ii][jj][i][j]=mmax(dp[ii][jj][i-1][j],dp[ii+(1<<(i-1))][jj][i-1][j]);
					}
				}
			}
		}
	}
}
int query(int x1,int x2,int y1,int y2){
	int k_x=log((double)x2-x1+1)/log(2.0),k_y=log((double)y2-y1+1)/log(2.0);
	int v1=mmax(dp[x1][y1][k_x][k_y],dp[x2-(1<<k_x)+1][y1][k_x][k_y]);
	int v2=mmax(dp[x1][y2-(1<<k_y)+1][k_x][k_y],dp[x2-(1<<k_x)+1][y2-(1<<k_y)+1][k_x][k_y]);
	return mmax(v1,v2);
}
int main(){
	int cas=1;
	while (scanf("%d%d",&N,&M)!=EOF){
		memset(sum,0,sizeof(sum));
		memset(qSum,0,sizeof(qSum));
		memset(t1,0,sizeof(t1));
		memset(t2,0,sizeof(t2));
		for (int i=1;i<=N;++i){
			for (int j=1;j<=M;++j){
				scanf("%d",&grid[i][j]);
			}
		}
		init();
		scanf("%d",&Q);
		printf("Case %d:\n",cas++);
		while (Q--){
			int a,b,ansi,ansj;
			double ansv=1e30;
			scanf("%d%d",&a,&b);
			a--,b--;
			for (int i=1;i+a<=N;++i){
				for (int j=1;j+b<=M;++j){
					int res=query(i,i+a,j,j+b),n=(a+1)*(b+1)-1;
					double subSum=sum[i+a][j+b]-sum[i+a][j-1]-(sum[i-1][j+b]-sum[i-1][j-1])-res;
					double subQSum=qSum[i+a][j+b]-qSum[i+a][j-1]-(qSum[i-1][j+b]-qSum[i-1][j-1])-res*res;
					double avg=subSum/n;
					double var=(subQSum+n*avg*avg-2*avg*subSum)/n;
					if(var<ansv){
						ansi=i;
						ansj=j;
						ansv=var;
					}
				}
			}
			printf("(%d, %d), %.2f\n",ansi,ansj,ansv);
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值