poj 2019 Cornfields【RMQ】

这道题是poj上一道还比较有名的二维RMQ (Range Minimum/Maximum Query 局部最大最小查询)问题,但是可能是这道题的数据设计的不是很好,所以可以直接暴力过,先上暴力AC版本的代码,之后会贴出RMQ正经版的代码。。。

RMQ问题(区间询问最值问题)。
题目大意:给出一个N*N矩形,每个格子上有一个价值。询问一个b*b的矩形在左上角的位置(x,y),(x+b-1,y+b-1)这一部分的最大值-最小值是多少。
题目分析:询问次数很多,但是矩形只有250且是正方形。二维的RMQ!

另外说一点,在poj评测时如果函数不返回值会编译错误,一定要int返回型定义main函数。

#include<stdio.h>

#define MAXN 260
#define bigInt 100000

int bigLand[MAXN][MAXN]; //大块土地

void work(int x,int y,int B)
{
	//从坐标为(x,y)的地方开始
	int i,j;
	int max=-1;
	int min=bigInt;
	for (i=x;i<=x+B-1;i++)
	{
		for(j=y;j<=y+B-1;j++)
		{
			if (bigLand[i][j]>max)
				max=bigLand[i][j];

			if(bigLand[i][j]<min)
				min=bigLand[i][j];
		}
	}

	printf("%d\n",max-min);
}

int main()
{
	//暴力解法
	int N; //大块土地边长
	int B; //指定的小块土地边长
	int K; //查询次数

	int x,y; //每次查询的坐标
	int i,j;

	while(scanf("%d%d%d",&N,&B,&K)!=EOF)
	{
		for (i=1;i<=N;i++)
			for(j=1;j<=N;j++)
				scanf("%d",&bigLand[i][j]);

		for(i=0;i<K;i++)
		{
			scanf("%d%d",&x,&y);
			work(x,y,B); //处理函数
		}
	}

	return 0;
}

下面是引用一位大牛的分析,讲的很不错

RMQ问题ST算法

ST算法O(nlogn)预处理,O(1)的查询指定区间的最值(以最小值为例)

基本上是把待求区间[l,r]分为两段长为len的区间

左边一段为[l,l+len-1],右边一段为[r-len+1,r]

len必须使得两段区间覆盖待求区间

设所求数组为w

那么,所求最小值就是两个区间的最小值间的最小值

即min(min{w[i],l<=i<=l+len-1},min{w[j],r-len+1<=j<=r})

若都在预先处理中先求得两个区间的最小值

则每次查询的复杂度都是O(1)

---

 

对len做一个限制:只能为2的幂

在预处理中求出所有mi[b][t] : 以b为起点,长为2^t的区间的最小值.

则求解min(min{w[i],l<=i<=l+len-1},min{w[j],r-len+1<=j<=r})

就变成min(mi[l][t],mi[r-2^t+1][r]),其中t可以由此得出,以保证两段区间可以覆盖待求区间:

t=ln(r-l+1)/ln(2)

---

 

可以看到mi[b][t]=min(mi[b][t-1],mi[b+2^(t-1)-1][t-1])

特别地对于所有mi[i][0],其值都是w[i];

由此自底向上推出所有的mi[b][t]

mi大小为n*logn,预处理时间复杂度为O(nlogn),查询时间复杂度为O(1)

 

个人观点,RMQ问题的ST算法就是构建多颗二叉树,只不过多颗二叉树没有共同的根结点,只有共同的叶子结点。。。

1<<n是把1 按2进制 左移n位,开始 1 的2进制表示是 0000 0001

1<<2是4,1<<3是8,所以1<<n相当于2^n

1<<16是65536

使用RMQ的AC代码:

#include <stdio.h>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <cmath>

#define MAXN 260

using namespace std;

int bigLand[MAXN][MAXN]; //大块土地
int pmax[MAXN][MAXN][11],pmin[MAXN][MAXN][11];

int max(int a,int b){ return a>b?a:b; }
int min(int a,int b){ return a<b?a:b; }

void init_rmq(int n)
{
	int i;
	for(i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            pmax[i][j][0]=pmin[i][j][0]=bigLand[i][j];

	for(i=1;i<=n;i++)
		for(int k=1;(1<<k)<=n;k++)
			for(int j=1;j+(1<<k)-1<=n;j++)
			{
				pmax[i][j][k]=max(pmax[i][j][k-1],pmax[i][j+(1<<(k-1))][k-1]);
				pmin[i][j][k]=min(pmin[i][j][k-1],pmin[i][j+(1<<(k-1))][k-1]);
			}
}

void work(int x,int y,int B)
{
	//从坐标为(x,y)的地方开始
	int k=(int)(log(double(B))/log(2.0));
    int maxans=-1; int minans=0x3f3f3f3f;
    int l=y,r=y+B-1;
    for(int i=x;i<x+B;i++)
    {
        maxans=max(maxans,max(pmax[i][l][k],pmax[i][r-(1<<k)+1][k]));
        minans=min(minans,min(pmin[i][l][k],pmin[i][r-(1<<k)+1][k]));
    }

	printf("%d\n",maxans-minans);
}

int main()
{
	//RMQ解法
	int N; //大块土地边长
	int B; //指定的小块土地边长
	int K; //查询次数

	int x,y; //每次查询的坐标
	int i,j;

	while(scanf("%d%d%d",&N,&B,&K)!=EOF)
	{
		for (i=1;i<=N;i++)
			for(j=1;j<=N;j++)
				scanf("%d",&bigLand[i][j]);

		init_rmq(N);

		for(i=0;i<K;i++)
		{
			scanf("%d%d",&x,&y);
			work(x,y,B); //处理函数
		}
	}

	return 0;
}
时间: 2024-11-08 20:12:35

poj 2019 Cornfields【RMQ】的相关文章

poj 3264 Balanced Lineup【RMQ】

这道题是poj上面RMQ最具代表性的一道题,没有之一 题目也基本上就是一个裸RMQ的算法,看到这道题也可以用线段树解决,决定还是要出一个线段树的版本的,一道题搞懂两个知识点,多好! RMQ(Range Minimum/Maximum Query)问题:      RMQ问题是求给定区间中的最值问题.当然,最简单的算法是O(n)的,但是对于查询次数很多(设置多大100万次),O(n)的算法效率不够.可以用线段树将算法优化到O(logn)(在线段树中保存线段的最值).不过,Sparse_Table算

poj 2229 Sumsets 【动态规划】

点击打开题目 Sumsets Time Limit: 2000MS   Memory Limit: 200000K Total Submissions: 13291   Accepted: 5324 Description Farmer John commanded his cows to search for different sets of numbers that sum to a given number. The cows use only numbers that are an i

poj 2773 Happy2006【容斥原理】

Happy 2006 Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 9936   Accepted: 3411 Description Two positive integers are said to be relatively prime to each other if the Great Common Divisor (GCD) is 1. For instance, 1, 3, 5, 7, 9...are al

poj 1503 Integer Inquiry【高精度】

这题总算是没有那么水的感觉了,不过还是水题,哈哈哈...题目主要是求高精度----大数的和,我专门写了一个add函数处理,sum和VeryLongIntegers是两个全局的变量,开始我还准备把sum也写成char型的字符串,但是考虑到结尾的'\0',那不是自找麻烦..果断换成int型数组,这样就容易处理多了. 在add函数中,我把VeryLongIntegers又反转了一次(即变成低位在前),也换成一个int型的数组,这样就变成两个int型数组的高精度加法了... 开始还觉得可能会WA一次,不

并查集专题【完结】

个人整理 并查集 [poj] 第一题 poj 1182 食物链 点击打开链接poj 1182 思路: 带权并查集 分析: 1 典型的带权并查集的题目 2 如果x和y是同类的关系认为是0,如果是x吃y那么关系认为是1,那么x和y的关系为2就说明y吃x 3 那么如果x和y同类则rank[x] == rank[y] , 如果x吃y那么rank[x] == (rank[y]+1)%3 , 注意mod3的使用 点击查看代码 第二题 poj 1308 Is It A Tree? 点击打开链接 poj 130

【Linux】linux命令大全

 [注意]:命令[compgen -b]可以列出所有当前系统支持的命令. 109个Linux命令 目录 1       文件管理... 5 1.1          basename. 5 1.2          cat 5 1.3          cd. 5 1.4          chgrp. 5 1.5          chmod. 6 1.6          chown. 7 1.7          comm.. 7 1.8          cp. 7 1.9       

【转】Java I/O流概念分析整理

转载地址:http://blog.csdn.net/yuebinghaoyuan/article/details/7388059  Java中的流,可以从不同的角度进行分类. 按照数据流的方向不同可以分为:输入流和输出流. 按照处理数据单位不同可以分为:字节流和字符流. 按照实现功能不同可以分为:节点流和处理流. 输出流: 输入流: 因此输入和输出都是从程序的角度来说的. 字节流:一次读入或读出是8位二进制. 字符流:一次读入或读出是16位二进制. 字节流和字符流的原理是相同的,只不过处理的单位

【android相关】【问题解决】R.java文件丢失

在进行android开发过程中,有时候,我们会遇到gen文件中R.java丢失的现象.重新build,或者clean工程,close并重新打开Project,但有时也没解决. 这可能是由于不小心把xml文件写错了,或者在编辑xml或者其他文件时候点击了run,或者clear过项目等...,反正,你会发现gen下面的R.java的文件找不到了. 其实,只要xml文件有问题,系统就不会给自动生成R.java文件,因为系统需要参照每个xml里的数据来生成R.java. 当然,如果项目较大,而layou

【近战】基于微博用户关系与行为的用户建模分析

以下为[近战]第一篇,基于微博用户关系与行为的用户建模分析. 用户建模是广告.推荐.搜索算法最基础也是最核心的技术问题之一,本报告将介绍新浪微博大数据挖掘团队如何综合利用社交关系和用户行为来建立用户模型.以下分享下精彩内容.   微博及大数据   微博作为中国最大的社交媒体平台,微博沉淀了海量的用户,内容,关系,和行为数据.   其中用户:注册人数10亿,月活人数1.98亿,日活人数:8900万.关系:关注关系近千亿,分组关系50亿+.内容:日增博文1亿+,日增原创4000万.行为:转发6000