每日一题----Cornfields--poj2019(附翻译和题解)

文章讲述了如何利用稀疏表技术解决关于在一个给定农场上,针对不同大小子矩阵找出最大和最小海拔差的问题,以帮助FJ选择最佳种植区域。
摘要由CSDN通过智能技术生成

Cornfields

Time Limit: 1000MSMemory Limit: 30000K
Total Submissions: 11679Accepted: 5362

Description

FJ has decided to grow his own corn hybrid in order to help the cows make the best possible milk. To that end, he's looking to build the cornfield on the flattest piece of land he can find.

FJ has, at great expense, surveyed his square farm of N x N hectares (1 <= N <= 250). Each hectare has an integer elevation (0 <= elevation <= 250) associated with it.

FJ will present your program with the elevations and a set of K (1 <= K <= 100,000) queries of the form "in this B x B submatrix, what is the maximum and minimum elevation?". The integer B (1 <= B <= N) is the size of one edge of the square cornfield and is a constant for every inquiry. Help FJ find the best place to put his cornfield.

Input

* Line 1: Three space-separated integers: N, B, and K.

* Lines 2..N+1: Each line contains N space-separated integers. Line 2 represents row 1; line 3 represents row 2, etc. The first integer on each line represents column 1; the second integer represents column 2; etc.

* Lines N+2..N+K+1: Each line contains two space-separated integers representing a query. The first integer is the top row of the query; the second integer is the left column of the query. The integers are in the range 1..N-B+1.

Output

* Lines 1..K: A single integer per line representing the difference between the max and the min in each query.

Sample Input

5 3 1
5 1 2 6 3
1 3 5 2 7
7 2 4 6 1
9 9 8 6 5
0 6 9 3 9
1 2

Sample Output

5

题目翻译:

玉米地
时间限制:1000ms内存限制:30000K
总提交:11679接受:5362
描述

FJ决定种植自己的杂交玉米,以帮助奶牛尽可能产出最好的牛奶。为此,他打算在他能找到的最平坦的土地上建造玉米地。

FJ花了很大的钱调查了他的N × N公顷(1 <= N <= 250)的正方形农场。每公顷都有一个整数海拔(0 <=海拔<= 250)。

FJ将向你的程序提供海拔高度和一组K (1 <= K <= 100,000)的查询,查询的形式是“在这个B x B子矩阵中,最大和最小海拔高度是多少?”整数B (1 <= B <= N)是正方形玉米地的一条边的大小,对于每次查询都是一个常数。帮FJ找到种植玉米地的最佳地点。
输入

*第一行:三个空格分隔的整数:N, B, K。

*第2行…N+1:每行包含N个空格分隔的整数。第2行表示第1行;第3行表示第2行,以此类推。每行上的第一个整数表示第1列;第二个整数表示第2列;等。

*第N+2行…N+K+1:每行包含两个空格分隔的整数,表示一个查询。第一个整数是查询的第一行;第二个整数是查询的左列。整数的取值范围是1 ~ N-B+1。
输出

*第1行…K:每行一个整数,表示每个查询中最大值和最小值之间的差值。
样例输入

5 3 1
5 1 2 6 3
1、3、5、2、7
7 2 4 6 1
9 9 8 6 5
0 6 9 3 9
1 2
样例输出

5

题解:

  1. 定义常量和变量:
#define maxn 300
#define inf 0x3f3f3f3f
int n, m, a[maxn][maxn], fmax1[maxn][maxn][15], fmin1[maxn][maxn][15];
int lb[maxn];

maxn 定义了数组的最大长度,inf 定义了一个较大的值用于初始化最小值。n 表示农场的大小,m 表示子矩阵的边长,a 是一个二维数组,用于存储农场每个地块的海拔值。fmax1fmin1 是稀疏表,用于存储每个子矩阵的最大值和最小值。lb 是一个数组,用于计算 log2。

  1. 计算 log2 函数:
 
void loglb()
{
    lb[0] = -1;
    for (int i = 1; i < maxn; i++)
    {
        lb[i] = (i & (i - 1)) ? lb[i - 1] : lb[i - 1] + 1;
    }
}

该函数用于计算 0 到 n 的 log2 值,存储在 lb 数组中。

这段代码是一个函数loglb(),用于计算数组lb[]的值。

首先,定义了一个全局数组lb[]用于保存计算结果。

然后,函数开始通过设置lb[0] = -1来初始化数组的第一个元素。

接下来,使用一个循环从i = 1开始,遍历数组的每个元素位置。

在循环中,使用三元条件运算符(? :)进行判断,如果i & (i - 1)为真,则将lb[i]的值设置为lb[i - 1];否则,将lb[i]的值设置为lb[i - 1] + 1

这样,经过循环迭代计算,最终得到了数组lb[]的值。

总结起来,函数loglb()的作用是计算并初始化数组lb[]的值,其中lb[i]表示i的二进制表示中最高位的位置。

  1. 创建稀疏表:
 
void st_create()
{
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            fmax1[k][i][0] = fmin1[k][i][0] = a[k][i];
    for (int k = 1; k <= n; k++)
        for (int j = 1; j <= lb[n]; j++)
            for (int i = 1; i + (1 << j) - 1 <= n; i++)
            {
                fmax1[k][i][j] = max(fmax1[k][i][j - 1], fmax1[k][i + (1 << (j - 1))][j - 1]);
                fmin1[k][i][j] = min(fmin1[k][i][j - 1], fmin1[k][i + (1 << (j - 1))][j - 1]);
            }
}

该函数用于创建稀疏表。首先,将每个地块的海拔值初始化为稀疏表的第 0 层。然后,通过动态规划的方式,计算出剩余层的最大值和最小值。

  1. 主函数:
 
int main()
{
    int k;
    loglb();
    scanf("%d%d%d", &n, &m, &k);
    
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            scanf("%d", &a[i][j]);
        }
    }
    st_create();

    while (k--)
    {
        int maxx = -1, minx = inf;
        int x, y;
        scanf("%d%d", &x, &y);
        int l = y, r = y + m - 1;
        int p = lb[m];
        for (int i = x; i < x + m; i++)
        {
            maxx = max(maxx, max(fmax1[i][l][p], fmax1[i][r - (1 << p) + 1][p]));
            minx = min(minx, min(fmin1[i][l][p], fmin1[i][r - (1 << p) + 1][p]));
        }
        printf("%d\n", maxx - minx);
    }
    
    return 0;
}

主函数首先调用 loglb() 函数计算 log2 值。然后,读取输入的农场大小 n、子矩阵的边长 m 和查询的数量 k。接下来,读取农场每个地块的海拔值,并调用 st_create() 函数创建稀疏表。

之后,通过循环处理每个查询。对于每个查询,读取查询的左上角位置 (x, y),计算出子矩阵的右下角位置 (x + m - 1, y + m - 1)。然后,通过稀疏表,找到子矩阵中的最大值和最小值,并计算它们的差值,即 maxx - minx。最后,输出查询的结果。

这段代码实现了稀疏表的预处理和查询操作,能够有效地找到子矩阵中的最大值和最小值,并计算它们的差值。

完整代码:

#include<iostream>
using namespace std;
#define maxn 300
#define inf 0x3f3f3f3f
int n, m, a[maxn][maxn], fmax1[maxn][maxn][15], fmin1[maxn][maxn][15];
int lb[maxn];

void loglb()
{
    lb[0] = -1;
    for (int i = 1; i < maxn; i++)
    {
        lb[i] = (i & (i - 1)) ? lb[i - 1] : lb[i - 1] + 1;
    }
}

void st_create()
{
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            fmax1[k][i][0] = fmin1[k][i][0] = a[k][i];
    for (int k = 1; k <= n; k++)
        for (int j = 1; j <= lb[n]; j++)
            for (int i = 1; i + (1 << j) - 1 <= n; i++)
            {
                fmax1[k][i][j] = max(fmax1[k][i][j - 1], fmax1[k][i + (1 << (j - 1))][j - 1]);
                fmin1[k][i][j] = min(fmin1[k][i][j - 1], fmin1[k][i + (1 << (j - 1))][j - 1]);
            }
}

int main()
{
    int k;
    loglb();
    scanf("%d%d%d", &n, &m, &k);
    
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            scanf("%d", &a[i][j]);
        }
    }
    st_create();

    while (k--)
    {
        int maxx = -1, minx = inf;
        int x, y;
        scanf("%d%d", &x, &y);
        int l = y, r = y + m - 1;
        int p = lb[m];
        for (int i = x; i < x + m; i++)
        {
            maxx = max(maxx, max(fmax1[i][l][p], fmax1[i][r - (1 << p) + 1][p]));
            minx = min(minx, min(fmin1[i][l][p], fmin1[i][r - (1 << p) + 1][p]));
        }
        printf("%d\n", maxx - minx);
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

善程序员文

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值