hdu4302 优先队列/线段树/set

题意

有一根长为L的管道和一个初始位置在最左端的动物,输入数据"0 x"表示此时在管道的左数x位置出现了一个食物,"1"表示动物想吃东西了:他会吃最近的食物,如果有多个最近的食物,就按照最近一次的移动方法移动,求所有命令结束后的动物移动路程。

思路

这个题目就是要迅速的找到比当前位置大的最小的那个数,和比当前位置小的最大的那个数(或者当前值本身),下面两个方法都是从这个思想入手的。

思路1:构造两个优先队列left和right.left是最大堆,堆顶即为在蛇左边离蛇最近的那块蛋糕;right为最小堆,堆顶为在蛇右边离蛇最近的那块蛋糕.模拟所有情况即可

思路2:线段树,存取每个位置上食物个数,并存区间最大值和最小值,对于一点,若没有食物,那么最大值为-1,最小值为INF,然后就是裸的线段树了.

附代码:

优先队列:

#include<iostream>
#include<queue>
#include<algorithm>
#include<functional>
#include<cstdio>
using namespace std;
priority_queue<int,vector<int>,greater<int> >Right;
priority_queue<int>Left;
int main(){
    int T;
    int cas=1;
    cin>>T;
    while(T--){
        while(!Left.empty())
            Left.pop();
        while(!Right.empty())
            Right.pop();
        int len,n;
        cin>>len>>n;
        int pos=0;//蛇当前位置
        int dir=1;//上次移动的方向
        int dis=0;//移动总距离
        for(int i=0;i<n;i++){
            int a,b;
            cin>>a;
            if(a==0){
                cin>>b;
                if(b<=pos)
                    Left.push(b);
                else
                    Right.push(b);
            }
            if(a==1){
                if(!Left.empty()&&!Right.empty()){
                    int pos1=Left.top();
                    int pos2=Right.top();
                    if(pos-pos1==pos2-pos){
                        if(dir==0){
                            Left.pop();
                            dis+=pos-pos1;
                            pos=pos1;
                        }
                        if(dir==1){
                            Right.pop();
                            dis+=pos-pos1;
                            pos=pos2;
                        }
                    }
                    else if(pos-pos1<pos2-pos){
                        Left.pop();
                        dis+=pos-pos1;
                        pos=pos1;
                        dir=0;
                    }
                    else if(pos-pos1>pos2-pos){
                        Right.pop();
                        dis+=pos2-pos;
                        pos=pos2;
                        dir=1;
                    }
                }
                else if(!Left.empty()){
                    int pos1=Left.top();
                    Left.pop();
                    if(pos!=pos1){
                        dis+=pos-pos1;
                        dir=0;
                        pos=pos1;
                    }
                }
            	else if(!Right.empty()){
                    int pos1=Right.top();
                	Right.pop();
                    dis+=pos1-pos;
                    dir=1;
                    pos=pos1;
                }
            }
        }
         printf("Case %d: %d\n",cas++,dis);
    }
    return 0;
}

线段树:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int MAXN=100000+100;
const int INF=0x3fffffff;
struct node
{
	int l,r,t,mx,mi;
}seg[MAXN*4];
void build(int i,int l,int r)
{
	seg[i].l=l,seg[i].r=r,seg[i].t=0;
	seg[i].mx=-1,seg[i].mi=INF;
	if(l==r)
	return;
	int m=(l+r)>>1;
	build(i*2,l,m),build(i*2+1,m+1,r);
}
void push_up(int i)
{
	if(seg[i].l==seg[i].r) return;
	seg[i].mx=max(seg[i*2].mx,seg[i*2+1].mx);
	seg[i].mi=min(seg[i*2].mi,seg[i*2+1].mi);
}
void add(int i,int p)
{
	if(seg[i].l==seg[i].r)
	{
		seg[i].t++;
		seg[i].mx=seg[i].mi=p;
		return;
	}
	int m=(seg[i].l+seg[i].r)>>1;
	if(p<=m) add(i*2,p);
	else add(i*2+1,p);
	push_up(i);
}
void del(int i,int p)
{
	if(seg[i].l==seg[i].r)
	{
		seg[i].t--;
		if(!seg[i].t)
		{
			seg[i].mx=-1;
			seg[i].mi=INF;
		}
		return;
	}
	int m=(seg[i].l+seg[i].r)>>1;
	if(p<=m) del(i*2,p);
	else del(i*2+1,p);
	push_up(i);
}
int query1(int i,int l,int r)
{
	if(seg[i].l==l&&seg[i].r==r)
	return seg[i].mx;
	int m=(seg[i].l+seg[i].r)>>1;
	if(r<=m) return query1(i*2,l,r);
	else if(l>m) return query1(i*2+1,l,r);
	else return max(query1(i*2,l,m),query1(i*2+1,m+1,r));
}
int query2(int i,int l,int r)
{
	if(seg[i].l==l&&seg[i].r==r)
	return seg[i].mi;
	int m=(seg[i].l+seg[i].r)>>1;
	if(r<=m) return query2(i*2,l,r);
	else if(l>m) return query2(i*2+1,l,r);
	else return min(query2(i*2,l,m),query2(i*2+1,m+1,r));
}
 
int main()
{
	int cas;
	scanf("%d",&cas);
	for(int k=1;k<=cas;k++)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		build(1,0,n);
		int x=0,dir=1,ans=0;
		while(m--)
		{
			int op;
			scanf("%d",&op);
			if(op==0)
			{
				int p;
				scanf("%d",&p);
				add(1,p);
			}
			else
			{
				int t1=query1(1,0,x),t2=query2(1,x,n);
				if(t1!=-1&&t2!=INF)
				{
					if(x-t1<t2-x)
					{
						dir=-1;
						del(1,t1);
						ans+=x-t1;
						x=t1;
					}
					else if(x-t1>t2-x)
					{
						dir=1;
						del(1,t2);
						ans+=t2-x;
						x=t2;
					}
					else
					{
						if(dir==1)
						{
							del(1,t2);
							ans+=t2-x;
							x=t2;
						}
						else
						{
							del(1,t1);
							ans+=x-t1;
							x=t1;
						}
					}
				}
				else if(t1!=-1&&t2==INF)
				{
					del(1,t1);
					ans+=x-t1;
					x=t1;
					dir=-1;
				}
				else if(t2!=INF&&t1==-1)
				{
					del(1,t2);
					ans+=t2-x;
					x=t2;
					dir=1;
				}
			}
		}
		printf("Case %d: %d\n",k,ans);
    }
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值