ZJU 2859 Matrix Searching
題目鏈接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2859


/**//*
題意:
給定一個n*n(n <= 300)的矩陣,然后是(T <= 10^6)次詢問,每次詢問某個子矩
陣中的最小值。

解法:
二維線段樹 或者 二維RMQ

思路:
一維線段樹是一顆完全二叉樹,那么二維線段樹無疑就是一顆完全四叉樹,換言
之,每個結點有四個兒子,這里為了空間不浪費,將所有結點記錄在一個一維數組中
,每個結點的四個兒子采用編號的方式存儲,在建樹之前將每個結點的信息全部初始
化,初始化的時候需要注意的是每次將當前結點的四個兒子清空,然后判斷它本身是
否是葉子結點,可以通過x和y區間端點是否重合來判斷,最后再來生成四個兒子編號
,然后往下遞歸,遞歸結束后根據四個兒子的最小值更新當前的最小值。再來看詢問
,和一維的情況類似,一維是對區間交,而二維則是對矩形交,如果詢問的二維區間
和當前結點管理的二維區間沒有交集,顯然不可能有最小值,直接返回inf,否則如果
詢問的二維區間完全包含了當前結點管理的二維區間,那么返回結點最小值。否則遞
歸當前結點的四個兒子,取最小值,回歸到根節點就得到了詢問區間的最值了。
需要注意的是在建樹的時候不要在葉子結點生成多余的兒子結點,這樣內存會多
一倍,如果開得不夠大有可能下標越界,開得太大有可能超內存。還有就是在二維線
段樹的結點上信息會多了不少,能節省空間盡量節省,比如每個結點管理的區間端點
不可能很大,所以不需要int,short就足夠了。
*/

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

#define maxn 310
#define inf (1<<30)-1


struct Tree
{
int Min; // 當前結點區間最小值
int son[4]; // 四個兒子在數組中的編號
short x[2], y[2]; // 當前結點管理的二維區間
}T[maxn*maxn*5];

int Tree_Id;

int n;
int mat[maxn][maxn];


void Build(int fat, int x0, int x1, int y0, int y1)
{
int i;

for(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(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);
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)
{
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);
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;
}


/**//*
題意:
給定一個n*n(n <= 300)的矩陣,然后是(T <= 10^6)次詢問,每次詢問某個子矩
陣中的最小值。
解法:
二維線段樹 或者 二維RMQ
思路:
一維線段樹是一顆完全二叉樹,那么二維線段樹無疑就是一顆完全四叉樹,換言
之,每個結點有四個兒子,這里為了空間不浪費,將所有結點記錄在一個一維數組中
,每個結點的四個兒子采用編號的方式存儲,在建樹之前將每個結點的信息全部初始
化,初始化的時候需要注意的是每次將當前結點的四個兒子清空,然后判斷它本身是
否是葉子結點,可以通過x和y區間端點是否重合來判斷,最后再來生成四個兒子編號
,然后往下遞歸,遞歸結束后根據四個兒子的最小值更新當前的最小值。再來看詢問
,和一維的情況類似,一維是對區間交,而二維則是對矩形交,如果詢問的二維區間
和當前結點管理的二維區間沒有交集,顯然不可能有最小值,直接返回inf,否則如果
詢問的二維區間完全包含了當前結點管理的二維區間,那么返回結點最小值。否則遞
歸當前結點的四個兒子,取最小值,回歸到根節點就得到了詢問區間的最值了。
需要注意的是在建樹的時候不要在葉子結點生成多余的兒子結點,這樣內存會多
一倍,如果開得不夠大有可能下標越界,開得太大有可能超內存。還有就是在二維線
段樹的結點上信息會多了不少,能節省空間盡量節省,比如每個結點管理的區間端點
不可能很大,所以不需要int,short就足夠了。
*/
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define maxn 310
#define inf (1<<30)-1

struct Tree
{
int Min; // 當前結點區間最小值
int son[4]; // 四個兒子在數組中的編號
short x[2], y[2]; // 當前結點管理的二維區間
}T[maxn*maxn*5];
int Tree_Id;
int n;
int mat[maxn][maxn];

void Build(int fat, int x0, int x1, int y0, int y1)
{
int i;
for(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(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);
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)
{
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);
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;
}posted on 2011-03-30 13:07 英雄哪里出來 閱讀(1423) 評論(0) 編輯 收藏 引用 所屬分類: 線段樹

