题目大意:
题目链接:http://poj.org/problem?id=3190
有条线段,将这些线段塞入一些集合中,要求每个集合的线段互不重合。求最小的集合数和方案。
思路:
很明显的贪心。
最容易想到的做法。现将线段以左端点排序,枚举每一条线段和每一个已有集合,如果可以将这条线段放进这个集合中就放。期望分60。
考虑使用小根堆维护,每次枚举牛就只要和堆顶元素做比较。时间复杂度。
代码:
#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;
}