HDU5493 Queue【线段树】

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5493


题目大意:

有 N 个人排队,每个人都忘记自己的位置。但是每个人都知道自己的身高 Hi 和自己

边或后边有 Ki 个比自己高的人。给你每个人的 Hi,Ki,求按身高满足最小序的情况。

如果不满足最小序的情况,则输出"impossible"。


解题思路:

题目要求输出身高字典序最小的情况,那么先对身高按从小到大排序。然后按身高从小

到大确定每个人的位置。由于每次都是从小到大,则每次放进的人都是比之前的人高的。

设当前放进的人是身高从小到大第 i 个人,如果要满足他前边或后边刚好有 k 个人比他

高,则:

如果前边有 k 个人比他高,则现在前边要空出 k 个位置来放置比他高的人,他就放置在

第 k+1 的位置上。

如果后边有 k 个人比他高,已知已经放置了 i-1 个人,除了他自己,还剩下 N - (i-1) - 1

个人。而后边还要放 k 个比他高的人,则前边还应该要空出 N - (i-1) - 1 - k 个位置,则

他自己放置在第 N - i - k + 1 的位置上

为了使身高的字典序最小,则每次都应该尽可能的靠前。所以应该选择这两种情况中的最

小情况,即 min(k,N - i - k)。

现在考虑不可能的情况。第一种在前边要空出 k 个比他高的位置,已经算上自己共有 i+k

个人,如果这个数应该小于等于 N 否则不能放置( i + k > N,则不能放置)。第二种在前边

空出 N - i - k 个位置,则 N - i - k > 0(同样是i + k > N,则不能放置)。

综上可得:如果对于当前要放的第 i个人,如果  i + k > N,则无解,输出"impossible"。


AC代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN = 100010;
const int INF = 0x3f3f3f3f;

int Ans[MAXN];
int Sum[MAXN << 2];

struct Node
{
    int h;
    int k;
}A[MAXN];

bool cmp(Node a,Node b)
{
    if(a.h != b.h)
        return a.h < b.h;
    return a.k < b.k;
}
void Build(int root,int L,int R)
{
    Sum[root] = R - L + 1;
    if(L == R)
        return;
    int Mid = (L + R) >> 1;
    Build(root << 1,L,Mid);
    Build(root << 1|1,Mid+1,R);
}

void Update(int root,int L,int R,int k,int v)
{
    Sum[root]--;
    if(L == R)
    {
        Ans[L] = v;
        return;
    }
    int Mid = (L + R) >> 1;
    if(Sum[root << 1] > k)
        Update(root<<1,L,Mid,k,v);
    else
        Update(root<<1|1,Mid+1,R,k-Sum[root<<1],v);
}

int main()
{
    int T,N,kase = 0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&N);
        for(int i = 1; i <= N; ++i)
            scanf("%d%d",&A[i].h,&A[i].k);
        sort(A+1,A+1+N,cmp);
        Build(1,1,N);
        bool flag = true;
        for(int i = 1; i <= N; ++i)
        {
            int k = A[i].k;
            int v = A[i].h;
            if(k+i > N) //不满足放置的情况
            {
                flag = false;
                break;
            }
            k = min(k,N-i-k);
            Update(1,1,N,k,v);
        }
        printf("Case #%d:",++kase);
        if( !flag )
            printf(" impossible\n");
        else
        {
            for(int i = 1; i <= N; ++i)
                printf(" %d",Ans[i]);
            printf("\n");
        }
    }

    return 0;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值