二分图+贪心优化 [2009国家集训队]最大收益

问题 B: [2009国家集训队]最大收益
时间限制: 1 Sec 内存限制: 259 MB
题目描述
给出N件单位时间任务,对于第i件任务,如果要完成该任务,需要占用[Si, Ti]间的某个时刻,且完成后会有Vi的收益。求最大收益。 N≤5000,1 ≤ Si ≤ Ti ≤ 108,1 ≤ Vi ≤ 108。 澄清:一个时刻只能做一件任务,做一个任务也只需要一个时刻。

输入
第一行一个整数N,表示可供选择的任务个数. 接下来的第二到第N+1行,每行三个数,其中第i+1行依次为Si,Ti,Vi

输出
输出最大收益

样例输入
4
1 1 2
2 2 2
1 2 3
1 3 1
样例输出
6
提示
共有四个任务,其中第一个任务只能在时刻1完成,第二个任务只能在时刻2做,第三个任务只能在时刻1或时刻2做,第四个任务可以在[1,3]内任一时刻完成,四个任务的价值分别为2、2、3和1。一种完成方案是:时刻1完成第一个任务,时刻2完成第三个任务,时刻3完成第四个任务,这样得到的总收益为2+3+1=6,为最大值。

如果把每一个任务与其可匹配的区间内所有点连边,时空间都会炸掉。但是每个任务只在一个点完成,这里就有了两个优化。
1.因为一个点只能满足一个任务,点与点间不会互相影响,所以先满足权值大的任务一定最优。贪心get
2.因此只会有至多N个时间点被利用,所以只要找出满足的N个点就好了。空间get
那么来说说怎么找.因为一个区间里有很多点,如果第一个点符合,就没必要再向后找了。按左端点排序任务,如果左端点已被标记,那么就标记后面第一个没被标记的就好了。(小优化:记录下每个任务可以从第几个点开始找能否匹配)
光这么打还是会TLE的。。亲身体验。。另一个优化:匹配二分图时,如果当前的时间点被匹配了,有两种选择,1.让当前任务和下一个点匹配;2.让当前时间点匹配的任务去和下一个时间点匹配。那么优化来了。设与当前时间点匹配的任务是A,而正在匹配的任务是B,如果A的右端点在B的右端点之前,那么把A向后边的时间点匹配,能成功那B也一定能成功,但A不成功B还可能成功。。。否则同理,所以只有B的右端点在A的右边,才去让B和后面的时间点匹配。否则直接让A去匹配就行了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int n,v[5005],pro[5005];
struct node{int l,r,x;ll h;}a[5005];
inline bool find(int x,int s)
{
       if(pro[s]>a[x].r)return 0;
       if(!v[s]){v[s]=x;return 1;}
       else
       {

           if(a[v[s]].r<a[x].r)return find(x,s+1);
           if(find(v[s],s+1)){v[s]=x;return 1;}
       }
       return 0;
}
inline bool cmpx(node x,node y){return x.l==y.l?x.r<y.r:x.l<y.l;}
inline bool cmph(node x,node y){return x.h>y.h;}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d%lld",&a[i].l,&a[i].r,&a[i].h);
    sort(a+1,a+n+1,cmpx);a[0].x=1;
    for(int i=1;i<=n;i++)
    {
        if(a[i].l<=pro[i-1])pro[i]=pro[i-1]+1;
        else pro[i]=a[i].l;
        a[i].x=a[i-1].x;
        while(pro[a[i].x]<a[i].l&&a[i].x<n)a[i].x++;
    }
    sort(a+1,a+n+1,cmph);
    ll ans=0;
    for(int i=1;i<=n;i++)if(find(i,a[i].x))ans+=a[i].h;
    cout<<ans;
}
posted @ 2017-10-06 20:19  Hzoi_QTY  阅读(207)  评论(0编辑  收藏  举报