Cornfields

题意:给出一个n阶的方阵,给你一个点,求以这个点为左上角的b*b方阵的最大值和最小值的差值。

三种做法,第一种在求解的过程中记录答案(这应该是叫做记忆化),一种方法是二维RMQ,还有优先队列。

1s内可进行的运算次数约为1e8,记忆化枚举最坏的情况下耗时约为2.5e8,如果数据弱一些就可以划过去了,然后这题还真的过了

#include<cstdio>
int n,b,k,i,j,x,y,maxm,minm,arr[300][300],ans[300][300];
int main(){
    scanf("%d%d%d",&n,&b,&k);
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            scanf("%d",&arr[i][j]);
    while(k--){
        scanf("%d%d",&x,&y);
        if(!ans[x][y]){
            maxm=0;
            minm=0x3f3f3f3f;
            for(i=1;i<=b;i++)
                for(j=1;j<=b;j++){
                    if(arr[i+x-1][j+y-1]<minm)minm=arr[i+x-1][j+y-1];
                    if(arr[i+x-1][j+y-1]>maxm)maxm=arr[i+x-1][j+y-1];
                }
            ans[x][y]=maxm-minm;
        }
        printf("%d\n",ans[x][y]);
    }
    return 0;
}

RMQ模板:(这方法不知道为什么我WA了,所以代码仅供参考qaq)
一维RMQ求得是区间区间的最值,用一个二维数组dq[i][k]来表示以i为左端点的连续2k个数字,即右端点为i+2k-1.则可以将其拆分成两个区间,(i,2k-1-1)和(ik-1,i+2k-1),原区间最大值为这两个区间的最大值,拆分后的区间又可以不断地拆分,直到长度为1
一维RMQ实现

for(int j=1;1<<j<=k;j++){
        for(int i=1;i+(1<<j)-1<=k;i++){
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }

二维RMQ只是在一维RMQ增加了一位,所以我们用四维数组dp[i][j][k][l]表示以(i,j)为左上角,(i+2k-1,j+2l-1)为右下角的矩阵,和一维RMQ一样,可以将大的矩阵拆分为小的矩阵,不断求出其最大值,直到矩阵为1*1方阵

//这个大家看看就好,我也是做题的时候学的二维RMQ,可能有问题,虽然过了自己的例子,但总归没用这个AC,
for (int j = 0; (1 << j) <=n; j++) {
		for (int l = 0; (1<<l) <= n; l++) {
			if (l == 0 && j == 0) continue;
			for (int row = 1; row + (1 << j) - 1 <= n; row++) {
				for (int col = 1; col + (1 << l) - 1 <= n; col++) {
					if (j==0) {//当有一维的长度为1是就是一个一维RMQ
						max_rix[row][col][j][l] = max(max_rix[row][col + (1 << (l - 1))][j][l - 1], max_rix[row][col][j][l - 1]);
						min_rix[row][col][j][l] = min(min_rix[row][col + (1 << (l - 1))][j][l - 1], min_rix[row][col][j][l - 1]);
					}
					else{
						max_rix[row][col][j][l] = max(max_rix[row + (1 << (j - 1))][col][j - 1][l], max_rix[row][col][j - 1][l]);
						min_rix[row][col][j][l] = min(min_rix[row + (1 << (j - 1))][col][j - 1][l], min_rix[row][col][j - 1][l]);

					}
				}
			}
		}
	}

最后是优先队列,用两个单调队列预处理,求出各个b*b矩阵的最大值和最小值,然后询问时直接使用即可,灵感来源与一道求子矩阵最大值和的题
题目链接
我的题解链接
AC代码

#include<cstdio>
#include<deque>

using namespace std;

int qmax[270][270], qmin[270][270], arr[270][270], ansmax[270][270],ansmin[270][270];

struct node {
	int id;
	int val;
};

int main() {
	int n, b, k;
	scanf("%d %d %d", &n, &b, &k);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			scanf("%d", &arr[i][j]);
		}
	}
	for (int i = 1; i <= n; i++) {
		deque<node> dqmax, dqmin;
		for (int j = 1; j <= n; j++) {
			while (!dqmax.empty() && arr[i][j] > dqmax.back().val) dqmax.pop_back();
			while (!dqmin.empty() && arr[i][j] < dqmin.back().val) dqmin.pop_back();
			while (!dqmax.empty() && j - dqmax.front().id >= b) dqmax.pop_front();
			while (!dqmin.empty() && j - dqmin.front().id >= b) dqmin.pop_front();
			dqmax.push_back({j,arr[i][j]});
			dqmin.push_back({j,arr[i][j]});
			if (j >= b) {
				ansmax[i][j - b + 1] = dqmax.front().val;
				ansmin[i][j - b + 1] = dqmin.front().val;
			}
		}
	}
	/*printf("\n");
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j+b-1 <= n; j++) {
			printf("%d ", ansmax[i][j]);
		}
		printf("\n");
	}
	printf("\n");*/
	for (int i = 1; i <= n; i++) {
		deque<node> dqmax, dqmin;
		for (int j = 1; j <= n; j++) {
			int ma = ansmax[j][i], mi = ansmin[j][i];
			while (!dqmax.empty() && ma > dqmax.back().val) dqmax.pop_back();
			while (!dqmin.empty() && mi < dqmin.back().val) dqmin.pop_back();
			while (!dqmax.empty() && j - dqmax.front().id >= b) dqmax.pop_front();
			while (!dqmin.empty() && j - dqmin.front().id >= b) dqmin.pop_front();
			dqmax.push_back({j,ma});
			dqmin.push_back({j,mi});
			if (j >= b) {
				ansmax[j-b+1][i] = dqmax.front().val;
				ansmin[j-b+1][i] = dqmin.front().val;
			}
		}
	}
	/*printf("\n");
	for (int i = 1; i+b-1 <= n; i++) {
		for (int j = 1; j + b - 1 <= n; j++) {
			printf("%d ", ansmax[i][j]);
		}
		printf("\n");
	}
	printf("\n");*/
	while (k--) {
		int l, r;
		scanf("%d %d", &l, &r);
		printf("%d\n", ansmax[l][r] - ansmin[l][r]);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值