ZJU 2859 Matrix Searching(二维线段树)


题意:
    给定一个n*n(n <= 300)的矩阵,然后是(T <= 10^6)次询问,每次询问某个子矩
阵中的最小值。


题解:建一颗二维线段树,表示这个矩阵的最小值,二维的线段树就可以解决,下面代码注释可以帮忙理解二维线段树的建树和查询

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=310;
const int inf=99999999;
struct Tree{
    int Min;//当前节点区间最小值
    int son[4];//四个儿子的编号
    short x[2],y[2];//当前节点管理的x,y两个维度的区间
}T[maxn*maxn*5];
int Tree_Id;
int n;
int mat[maxn][maxn];
//fat为当前节点的坐标
void Build(int fat,int x0,int x1,int y0,int y1){
    int i;
    //*初始化操作
    for(int i=0;i<4;i++){
        T[fat].son[i]=0;
    }
    //*/
    T[fat].x[0]=x0;T[fat].x[1]=x1;
    T[fat].y[0]=y0;T[fat].y[1]=y1;
    if(x0 == x1&& y0==y1){
        T[fat].Min = mat[x0][y0];//到达叶子节点
        return;
    }
    for(int i=0;i<4;i++){
        T[fat].son[i]=Tree_Id++;
    }
    int xmid = (x0+x1)>>1;
    int ymid = (y0+y1)>>1;
    Build(T[fat].son[0],x0,xmid,y0,ymid);//第一个节点
    Build(T[fat].son[1], x0, xmid,   (ymid<y1) ? ymid+1 : ymid, y1);//第二个节点  联想一下4 5两个所以加判断
    Build(T[fat].son[2], (xmid<x1) ? xmid+1 : xmid, x1, y0, ymid);//第三个节点
    Build(T[fat].son[3], (xmid<x1) ? xmid+1 : xmid, x1, (ymid<y1) ? ymid+1 : ymid, y1);//第四个节点
    //进行二维线段树的操作,比如求最值、求和
    T[fat].Min = T[T[fat].son[0]].Min;
    for(i = 1; i < 4; i++) {
        if(T[T[fat].son[i]].Min < T[fat].Min)
            T[fat].Min = T[T[fat].son[i]].Min;
    }
}
int Query(int fat, int x0, int x1, int y0, int y1) {
    //如果在区间外直接返回一个inf最大值
    if(x0 > T[fat].x[1] || x1 < T[fat].x[0]
    || y0 > T[fat].y[1] || y1 < T[fat].y[0]) {
        return inf;
    }
    //完全包含在询问区间里面则
    if(x0 <= T[fat].x[0] && T[fat].x[1] <= x1
        && y0 <= T[fat].y[0] && T[fat].y[1] <= y1) {
        return T[fat].Min;
    }

    int i;
    int Min = inf;
    for(i = 0; i < 4; i++) {
        int v = Query(T[fat].son[i], x0, x1, y0, y1);
        //访问它4个儿子
        if(v < Min)
            Min = v;
    }
    return Min;
}
int main()
{
    int t;
    int i, j;

    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        Tree_Id = 0;
        for(i = 1; i <= n; i++) {
            for(j = 1; j <= n; j++) {
                scanf("%d", &mat[i][j]);
            }
        }
        Tree_Id = 2;
        Build(1, 1, n, 1, n);

        int m;
        scanf("%d", &m);

        while(m--) {
            int r[2], c[2];
            scanf("%d %d %d %d", &r[0], &c[0], &r[1], &c[1]);
            printf("%d\n", Query(1, r[0], r[1], c[0], c[1]));
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值