您的位置:首页 > Web前端

POJ 2761 Feed the dogs

2014-02-28 21:18 288 查看
题目大意:

Wind爱狗,并且有n条狗(n < 100,001),佳佳不爱狗,但是爱Wind,所以他每天需要替Wind喂狗。当然了,他自有特别的喂狗方式,每到中饭时间够就会按照编号站成一排,从左到有一次为1, 2, 3...n号,每条狗都会被佳佳赋予一个“可爱值”,可爱值互不相同,喂的时候佳佳会先选出一个编号区间[i号-j号],然后在该区间中选择“可爱值”第k大的狗喂食,这样可以避免多次喂同样几条狗导致狗被喂死(这会让Wind大发雷霆,后果很严重)。

喂食会有m次(m < 50,001),每次都会指定选择区间(i, j)和k(区间内“可爱值”第k大的狗),先求出每次被喂食的狗的“可爱值”,选中的区间可能会相互重叠,只有一个测例。

题目链接

使用规模平衡二叉树SBT(Size Balanced Tree):

注释代码:

/*
* Problem ID : POJ 2761 Feed the dogs
* Author     : Lirx.t.Una
* Language   : C++
* Run Time   : 2032 ms
* Run Memory : 3248 KB
*/

#include <stdlib.h>
#include <stdio.h>

//表示树的结点为空
#define	NILL	0

#define	TRUE	1
#define	FALSE	0

//maximum number of dogs
//狗的最大数量
#define	MAXDOGN		100001
//maximum number of feedings
//喂食的最大次数
#define	MAXFEEDN	50001

struct	BFeeding {//Feeding body,
//关于喂食的结构体

int		l;//left,喂食的区间左端
int		r;//right,喂食的区间右端
int		k;//选择可爱值第k大的狗进行喂食
int		ord;//ordinal,指明本次是第几次喂食
};

typedef struct BFeeding		Feeding;
typedef	struct BFeeding *	Feed;
typedef	struct BFeeding **	PtFeed;

typedef	int		BOOL;
typedef	int		Tree;//使用数组实现SBT

Feeding		bf[MAXFEEDN];
Feed		f[MAXFEEDN];
int			ans[MAXFEEDN];//存放每次喂食的答案
//即每次第k大的狗的可爱值

//以下四项为SBT树中的结点
//其中树结点i中包含了四个域
//key[i]可爱值
//siz[i]即size,即该棵树的大小(总共包含几个结点)
//lft[i]、rht[i],该结点的左右子树
int			key[MAXDOGN] = { 0 };
int			siz[MAXDOGN] = { 0 };
Tree		lft[MAXDOGN] = { NILL };
Tree		rht[MAXDOGN] = { NILL };

int			dog[MAXDOGN];//dog[i]表示第i条狗的可爱值

Tree
RotL(Tree k2) {//AVL左旋

Tree k1;

k1		= lft[k2];
lft[k2]	= rht[k1];
rht[k1]	= k2;

siz[k1]	= siz[k2];
siz[k2] = siz[ lft[k2] ] + siz[ rht[k2] ] + 1;

return k1;
}

Tree
RotR(Tree k2) {//AVL右旋

Tree k1;

k1		= rht[k2];
rht[k2] = lft[k1];
lft[k1]	= k2;

siz[k1]	= siz[k2];
siz[k2]	= siz[ lft[k2] ] + siz[ rht[k2] ] + 1;

return k1;
}

Tree
RotLR(Tree t) {//AVL左右双旋

rht[t] = RotL( rht[t] );

return RotR(t);
}

Tree
RotRL(Tree t) {//AVL右左双旋

lft[t] = RotR( lft[t] );

return RotL(t);
}

Tree
Maintain( Tree tree, BOOL cmp ) {//维护
//SBT的插入其实都是普通的BST插入,在每次
//插入后都会使用该函数对SBT树进行维护
//以调整它的高度,使之平衡

if ( cmp )//表示插入的可爱值k小于tree结点的可爱值
//此时该结点必定插入到了tree的左子树,因此需要
//检查左子树是否过大

if ( siz[ lft[ lft[tree] ] ] > siz[ rht[tree] ] )
tree = RotL(tree);//若左子树的左子树规模大于右子树则需左旋
else
if ( siz[ rht[ lft[tree] ] ] > siz[ rht[tree] ] )
tree = RotRL(tree);//若左子树的右子树规模大于右子树则需右左旋
else
return tree;//否则就表示满足平衡条件,可以直接退出函数
else//对于插入到右子树的情形也是相同的
if ( siz[ rht[ rht[tree] ] ] > siz[ lft[tree] ] )
tree = RotR(tree);
else
if ( siz[ lft[ rht[tree] ] ] > siz[ lft[tree] ] )
tree = RotLR(tree);
else
return tree;

//即使旋转过也并不代表概述一定就满足平衡条件了,需要继续探测并调整

//当旋转后左右子仍存在的,则继续向树的最外两侧探测和调整
if ( lft[tree] )
lft[tree] = Maintain( lft[tree], TRUE );

if ( rht[tree] )
rht[tree] = Maintain( rht[tree], FALSE );

//此时左右子树已经完美平衡了,但顶点本身可能离平衡只有微小的距离
//因此此次维护只是做一点点微调,不会耗多少时间的
tree = Maintain( tree, TRUE );//左微调一次

return Maintain( tree, FALSE );//最后再右微调一次
}

Tree
Insert( Tree tree, int node, int k ) {

if ( !tree ) {

key[node] = k;
siz[node] = 1;

lft[node] = NILL;
rht[node] = NILL;

return node;
}

siz[tree]++;

if ( k < key[tree] )
lft[tree] = Insert( lft[tree], node, k );
else
rht[tree] = Insert( rht[tree], node, k );

//到这之前都是很平常的BST插入
return Maintain( tree, k < key[tree] );
}

Tree
Remove( Tree tree, int k ) {

if ( !tree )
return NILL;

siz[tree]--;//由于题目的限制,每次删除的结点
//必定都在树中,因此不需要再写Find函数判断
//待删的结点是否在树中了,因此可以直接--

if ( k == key[tree] ) {//若当前结点就是待删结点

if ( !lft[tree] || !rht[tree] )//对于左右子树有空的
return lft[tree] + rht[tree];//直接返回非空的
//或者两者都是空的,则就直接删除即可

int node;

//若左右子树都非空,则可以将左子树中最大的结点
//即右下角的结点作为根结点(树顶)代替老的树顶
//然后再删除该结点就行了

//先找到该结点(由于它是左子树中最大的结点,因此可以作为新树顶)
node = lft[tree];
while ( rht[node] )
node = rht[node];

key[tree] = key[node];//替代
lft[tree] = Remove( lft[tree], key[node] );//再删除该结点

return tree;
}

if ( k < key[tree] )
lft[tree] = Remove( lft[tree], k );
else
rht[tree] = Remove( rht[tree], k );

return tree;
}

int
Rank( Tree tree, int k ) {//按照可爱值大小排名

int		mid;

mid = siz[ lft[tree] ] + 1;//树顶是第mid大的

if ( k == mid )
return key[tree];

if ( k < mid )
return Rank( lft[tree], k );
else
return Rank( rht[tree], k - mid );
}

int
fcmp(const void *a, const void *b) {

Feed	fa, fb;

fa = *(PtFeed)a;
fb = *(PtFeed)b;

//排序时按照喂食区间的左端从小到大排
if ( fa->l - fb->l )
return fa->l - fb->l;
else//若左端相等则按照右端从小到大排
return fa->r - fb->r;
}

int
main() {

int		n, m;//狗总数和喂食总次数
int		i, j;//计数变量
int		l, r;//先区间left - right
int		pl, pr;//上衣区间previous left - previous right

int		node;//插入的结点号
Tree	tree;//SBT树

scanf("%d%d", &n, &m);
for ( i = 1; i <= n; i++ )
scanf("%d", dog + i);
for ( i = 1; i <= m; i++ ) {

scanf("%d%d%d", &bf[i].l, &bf[i].r, &bf[i].k);
bf[i].ord = i;
f[i]	  = bf + i;//利用指针数组对原数组排序,省时间
}
//具体做法是对于每个区间,都构建一个该区间的SBT树
//然后对该数求rank,即可得到第k大的狗了
//但是为了避免重复构建相同区间的SBT(或者是重合的)
//对喂食进行排序(左端从小到大,若相同则右端从小到大)
//可以避免对重叠的区间重复构造SBT树
qsort(f + 1, m, sizeof(Feed), &fcmp);

//初始化
tree = NILL;
node = 0;
pl	 = 1;
pr	 = 0;

for ( i = 1; i <= m; i++ ) {

l = f[i]->l;
r = f[i]->r;

//×表示该区间需要删除
//*表示该区间需要构建
//空表示该区间什么都不用做
//不变表示该区间保留,无需删除和构建
if ( l > pr ) {// pl×××pr  空  l****r

for ( j = pl; j <= pr; j++ )
tree = Remove( tree, dog[j] );
for ( j = l; j <= r; j++ )
tree = Insert( tree, ++node, dog[j] );
}
else
if ( r > pr ) {// pl×××l 不变 pr****r

for ( j = pl; j < l; j++ )
tree = Remove( tree, dog[j] );
for ( j = pr + 1; j <= r; j++ )
tree = Insert( tree, ++node, dog[j] );
}
else {// pl×××l 不变 r×××pr

for ( j = pl; j < l; j++ )
tree = Remove( tree, dog[j] );
for ( j = pr + 1; j < r; j++ )
tree = Remove( tree, dog[j] );
}

pl = l;
pr = r;

ans[ f[i]->ord ] = Rank( tree, f[i]->k );
}

for ( i = 1; i <= m; i++ )
printf("%d\n", ans[i]);

return 0;
}


无注释代码:

#include <stdlib.h>
#include <stdio.h>

#define	NILL	0
#define	TRUE	1
#define	FALSE	0

#define	MAXDOGN		100001
#define	MAXFEEDN	50001

struct	BFeeding {

int		l;
int		r;
int		k;
int		ord;
};

typedef struct BFeeding		Feeding;
typedef	struct BFeeding *	Feed;
typedef	struct BFeeding **	PtFeed;

typedef	int		BOOL;
typedef	int		Tree;

Feeding		bf[MAXFEEDN];
Feed		f[MAXFEEDN];
int			ans[MAXFEEDN];

int			key[MAXDOGN] = { 0 };
int			siz[MAXDOGN] = { 0 };
Tree		lft[MAXDOGN] = { NILL };
Tree		rht[MAXDOGN] = { NILL };

int			dog[MAXDOGN];

Tree
RotL(Tree k2) {

Tree k1;

k1		= lft[k2];
lft[k2]	= rht[k1];
rht[k1]	= k2;

siz[k1]	= siz[k2];
siz[k2] = siz[ lft[k2] ] + siz[ rht[k2] ] + 1;

return k1;
}

Tree
RotR(Tree k2) {

Tree k1;

k1		= rht[k2];
rht[k2] = lft[k1];
lft[k1]	= k2;

siz[k1]	= siz[k2];
siz[k2]	= siz[ lft[k2] ] + siz[ rht[k2] ] + 1;

return k1;
}

Tree
RotLR(Tree t) {

rht[t] = RotL( rht[t] );

return RotR(t);
}

Tree
RotRL(Tree t) {

lft[t] = RotR( lft[t] );

return RotL(t);
}

Tree
Maintain( Tree tree, BOOL cmp ) {

if ( cmp )
if ( siz[ lft[ lft[tree] ] ] > siz[ rht[tree] ] )
tree = RotL(tree);
else
if ( siz[ rht[ lft[tree] ] ] > siz[ rht[tree] ] )
tree = RotRL(tree);
else
return tree;
else
if ( siz[ rht[ rht[tree] ] ] > siz[ lft[tree] ] )
tree = RotR(tree);
else
if ( siz[ lft[ rht[tree] ] ] > siz[ lft[tree] ] )
tree = RotLR(tree);
else
return tree;

if ( lft[tree] )
lft[tree] = Maintain( lft[tree], TRUE );

if ( rht[tree] )
rht[tree] = Maintain( rht[tree], FALSE );

tree = Maintain( tree, TRUE );

return Maintain( tree, FALSE );
}

Tree
Insert( Tree tree, int node, int k ) {

if ( !tree ) {

key[node] = k;
siz[node] = 1;

lft[node] = NILL;
rht[node] = NILL;

return node;
}

siz[tree]++;

if ( k < key[tree] )
lft[tree] = Insert( lft[tree], node, k );
else
rht[tree] = Insert( rht[tree], node, k );

return Maintain( tree, k < key[tree] );
}

BOOL
Find( Tree tree, int k ) {

if ( !tree )
return FALSE;

if ( k == key[tree] )
return TRUE;

if ( k < key[tree] )
return Find( lft[tree], k );
else
return Find( rht[tree], k );
}

Tree
Remove( Tree tree, int k ) {

if ( !tree )
return NILL;

siz[tree]--;

if ( k == key[tree] ) {

if ( !lft[tree] || !rht[tree] )
return lft[tree] + rht[tree];

int node;

node = lft[tree];
while ( rht[node] )
node = rht[node];

key[tree] = key[node];
lft[tree] = Remove( lft[tree], key[node] );

return tree;
}

if ( k < key[tree] )
lft[tree] = Remove( lft[tree], k );
else
rht[tree] = Remove( rht[tree], k );

return tree;
}

int
Rank( Tree tree, int k ) {

int		mid;

mid = siz[ lft[tree] ] + 1;

if ( k == mid )
return key[tree];

if ( k < mid )
return Rank( lft[tree], k );
else
return Rank( rht[tree], k - mid );
}

int
fcmp(const void *a, const void *b) {

Feed	fa, fb;

fa = *(PtFeed)a;
fb = *(PtFeed)b;

if ( fa->l - fb->l )
return fa->l - fb->l;
else
return fa->r - fb->r;
}

int
main() {

int		n, m;
int		i, j;
int		l, r;
int		pl, pr;

int		node;
Tree	tree;

scanf("%d%d", &n, &m);
for ( i = 1; i <= n; i++ )
scanf("%d", dog + i);
for ( i = 1; i <= m; i++ ) {

scanf("%d%d%d", &bf[i].l, &bf[i].r, &bf[i].k);
bf[i].ord = i;
f[i]	  = bf + i;
}
qsort(f + 1, m, sizeof(Feed), &fcmp);

tree = NILL;
node = 0;
pl	 = 1;
pr	 = 0;

for ( i = 1; i <= m; i++ ) {

l = f[i]->l;
r = f[i]->r;

if ( l > pr ) {

for ( j = pl; j <= pr; j++ )
tree = Remove( tree, dog[j] );
for ( j = l; j <= r; j++ )
tree = Insert( tree, ++node, dog[j] );
}
else
if ( r > pr ) {

for ( j = pl; j < l; j++ )
tree = Remove( tree, dog[j] );
for ( j = pr + 1; j <= r; j++ )
tree = Insert( tree, ++node, dog[j] );
}
else {

for ( j = pl; j < l; j++ )
tree = Remove( tree, dog[j] );
for ( j = pr + 1; j < r; j++ )
tree = Remove( tree, dog[j] );
}

pl = l;
pr = r;

ans[ f[i]->ord ] = Rank( tree, f[i]->k );
}

for ( i = 1; i <= m; i++ )
printf("%d\n", ans[i]);

return 0;
}


单词解释:

pretty:adj, 漂亮的,可爱的

lunchtime:n, 午饭时间

leftmost/rightmost:adj, 最左边的/左右边的

afterefftect:n, 后果

hence:adj, 因此

intersect with:和...相交
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: