bzoj 2034 [2009国家集训队]最大收益
这个题,我想的是用一些神奇的线段树做法,根本没想匹配
但是好像很显然啊
(论文写的也太长了...)
给的l,r范围很大,其实有用的只有n个时刻
那可以先离散一下
然后把 各个线段按照权值从大到小排序,一个一个选
有矛盾一定选择权值大的,所以前面选定的集合一定是最优的
然后自己论文里说的check函数,看有没有空余的时刻,有就加上,没有就跳过
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int N=7006;
struct son
{
int l,r,v,s;
}ji[N];
bool ok_l(son a,son b)
{
if(a.l==b.l)
return a.r<b.r;
return a.l<b.l;
}
bool ok_v(son a,son b)
{
return a.v>b.v;
}
int n;
int pos[N];// 存离散后的点
int match[N];// 第i个活跃点选的任务
bool fin(int x,int t)
{
if(pos[t]>ji[x].r)
return 0;
if(match[t]==-1)
{
match[t]=x;
return 1;
}
int tt=match[t];
if(ji[x].r>ji[tt].r)// 贪心
return fin(x,t+1);
else
if(fin(tt,t+1))
{
match[t]=x;
return 1;
}
return 0;
}
int main(){
//freopen("in.in","r",stdin);
mem(match,-1);
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d%d%d",&ji[i].l,&ji[i].r,&ji[i].v);
sort(ji+1,ji+1+n,ok_l);
pos[0]=0;
for(int i=1;i<=n;++i)
pos[i]=max(pos[i-1]+1,ji[i].l);
ji[1].s=1;
for(int i=2;i<=n;++i)
{
ji[i].s=ji[i-1].s;
while( pos[ji[i].s]<ji[i].l&&ji[i].s<n )++ji[i].s;
}
sort(ji+1,ji+1+n,ok_v);
ll ans=0;
for(int i=1;i<=n;++i)
if(fin(i,ji[i].s))
ans+=ji[i].v;
cout<<ans;
}