Stall Reservations【贪心】【堆】

题目大意:

题目链接:http://poj.org/problem?id=3190
n 条线段,将这些线段塞入一些集合中,要求每个集合的线段互不重合。求最小的集合数和方案。


思路:

很明显的贪心。
最容易想到 O ( n 2 ) 的做法。现将线段以左端点排序,枚举每一条线段和每一个已有集合,如果可以将这条线段放进这个集合中就放。期望分60。
考虑使用小根堆维护,每次枚举牛就只要和堆顶元素做比较。时间复杂度 O ( n l o g n )


代码:

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;

int n,s[60001],m[60001],tot;

struct node
{
    int begin,end,num,k;
    //begin表示左端点,end表示右端点,num表示原来是第几条线段,k表示放在了第几个集合
}c[60001];

bool cmp1(node x,node y)
{
    return x.begin<y.begin;
}

bool cmp2(node x,node y)
{
    return x.num<y.num;
}

void down(int x)  //下移
{
    int y=x*2;
    while ((y<=tot&&s[y]<s[x])||(y+1<=tot&&s[y+1]<s[x]))
    {
        if (s[y+1]<s[y]&&y<tot) y++;
        swap(s[x],s[y]);
        swap(m[x],m[y]);
        x=y;
        y=x*2;
    }
}

void up(int x)  //上移
{
    int y=x/2;
    while (y>=1&&s[y]>s[x])
    {
        swap(s[x],s[y]);
        swap(m[x],m[y]);
        x=y;
        y=x/2;
    }
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d%d",&c[i].begin,&c[i].end);
        c[i].num=i;
    }
    sort(c+1,c+1+n,cmp1);
    s[1]=c[1].end;  //第一个之间放入
    c[1].k=1;
    tot=1;
    m[1]=1;

    for (int i=2;i<=n;i++)
     if (s[1]<c[i].begin)  //可以放进去
     {
        s[1]=c[i].end;
        c[i].k=m[1];
        down(1);
     }
     else  //不可以放进去
     {
        s[++tot]=c[i].end;
        c[i].k=tot;
        m[tot]=tot;
        up(tot);
     }
    printf("%d\n",tot);
    sort(c+1,c+1+n,cmp2);
    for (int i=1;i<=n;i++)
     printf("%d\n",c[i].k);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/SSL_ZYC/article/details/81662209