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;
}
23333

 

原文地址:https://www.cnblogs.com/A-LEAF/p/7623378.html