zoukankan      html  css  js  c++  java
  • [2009国家集训队]最大收益

    [2009国家集训队]最大收益

    题目

    给出N件单位时间任务,对于第i件任务,如果要完成该任务,需要占用[Si, Ti]间的某个时刻,且完成后会有Vi的收益。求最大收益。 N≤5000,1 ≤ Si ≤ Ti ≤ 108,1 ≤ Vi ≤ 108。 澄清:一个时刻只能做一件任务,做一个任务也只需要一个时刻。

    INPUT

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

    OUTPUT

    输出最大收益

    SAMPLE

    INPUT

    4

    1 1 2

    2 2 2

    1 2 3

    1 3 1

    OUTPUT

    6

    解题报告

    首先我们发现,如果找所有出现过的点,会有基础的一个$O(10^{8})$的复杂度,显然我们需要找到一些时间点来代替所有的时间点

    也就是说,我们需要找到一些时间点使得只在这些时间做任务得到的最大价值不变

    我们可以这样找:

    初始时,所有点都没有标记,即都可以成为有用的点,为了方便,我们将其称之为活跃点$(Active)$,接着,对于每一个任务,我们找到最小的时间点$time$,使得$timegeqslant S_{i}$,且$k$未被标记,那么我们将其标记,最后就会得到$n$个活跃点

    具体求法:

    按$S_{i}$从小到大排序,然后$O(n)$扫一遍,$Active[i]=max(Active[i-1]+1,S_{i})$

    所以接下来我们要做的就是,把这些活跃点与工作相匹配

    跑二分图匹配即可

     1 #include<algorithm>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 using namespace std;
     6 inline int read(){
     7     int sum(0);
     8     char ch(getchar());
     9     for(;ch<'0'||ch>'9';ch=getchar());
    10     for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar());
    11     return sum;
    12 }
    13 typedef long long L;
    14 struct job{
    15     int s,e;
    16     L v;
    17 }a[5005];
    18 inline bool cmp1(const job &a,const job &b){
    19     return a.s<b.s;
    20 }
    21 inline bool cmp2(const job &a,const job &b){
    22     return a.v>b.v;
    23 }
    24 int n;
    25 int active[5005],pp[5005];
    26 bool vis[5005];
    27 L ans;
    28 inline bool find(int x,int tim){
    29     if(active[tim]>a[x].e)
    30         return false;
    31     if(pp[tim]==0){
    32         pp[tim]=x;
    33         return true;
    34     }
    35     if(a[x].e<a[pp[tim]].e){
    36         if(find(pp[tim],tim+1)){
    37             pp[tim]=x;
    38             return true;
    39         }
    40     }
    41     else{
    42         if(find(x,tim+1))
    43             return true;
    44     }
    45     return false;
    46 }
    47 int main(){
    48     n=read();
    49     for(int i=1;i<=n;++i)
    50         a[i].s=read(),a[i].e=read(),a[i].v=read();
    51     sort(a+1,a+n+1,cmp1);
    52     for(int i=1;i<=n;++i)
    53         active[i]=max(active[i-1]+1,a[i].s);
    54     sort(a+1,a+n+1,cmp2);
    55     for(int i=1;i<=n;++i){
    56         int cnt(1);
    57         while(active[cnt]<a[i].s)
    58             ++cnt;
    59         if(find(i,cnt))
    60             ans+=a[i].v;
    61     }
    62     printf("%lld",ans);
    63 }
    View Code

    后记

    在集训队的解题报告中看到了这样一段话,突然觉得有些触动:

    题目模型

    不妨交代一下我出题的灵感吧。大概几个月前与邓原同学的交谈中知道这样一道题目:在CS游戏中,有n个敌人于时刻0同时出现,并且每个人有一个消失时间和一个价值,你可以在消失时间前任一时刻消灭他。现在你每一时刻可以射击一个人并获得相应的价值,求最后的最大总价值。这题用堆很容易解出来了。当时我就想能不能改成每个人都有出现时间和消失时间,然后求总价值,于是就形成了这道题。 

    其实这题的实际应用还是蛮普遍的。比如说,我们经常要选择做一些事情,有的事情逾期就不能做了,有的事情要等时机成熟。一个人活在世上最基本的就是去做事,去感受,去体验付出与收获。人不可能什么事情都做,什么事情都完成得很好,这便有了取舍;做事情要花时间,这便有了对时刻点的占用;一件事也不是任何时刻都可以做的,可能在某段时间才能够完成,这段时间可能很长,可能稍纵即逝,这便有了时限;完成一件事会有收获,可以是物质上的,也可以是精神上的,这便有了收益。这里我们抽象出一个最简单、最基本的模型,就成为了这一道题目。 

     实际当中解决这些问题并不是容易的。一般看来我们会先做比较“重要”的事情,可是何谓之“重要”?假如单看它的价值,那么可能出现这样的情况:一件事情价值很大,而截止日期还有很久,一件事情的截止时间很短,机会稍纵即逝,价值却偏偏没有前一件大。如果先做价值大的,那很可能就做不了另一件事情,而实际上我们可以先做价值小的,然后再做价值大的。但同样,单看它的结束时间也是不行的,必须综合考虑。这时我便试图设计算法来解决这个问题,这个过程同样不容易。  读者可以试图考虑这样的更一般的问题:假如事件还有一定的完成时间,即要在[S,T]间的连续或不连续的P个时刻完成;假如一个事件的完成时间限制不是一个区间,而是很多个区间;假如完成了某些任务才能做另一些任务等等。本题中,实际上仅有价值和时间两个约束,若是实际中可能还有更多更多的限制、困扰和其他影响因素。大千世界是难以用简单的数学模型来描述的。既然这样,人又何必要求完美,只要活过、做过,人生都是五彩斑斓的。

    人又何必要追求完美,活成自己,他人的眼光,世界的评价,仅此而已

    我即是我

  • 相关阅读:
    GridView只显示日期问题
    自定义一个选择日期的用户控件
    母版页所带来的路径问题
    C#之旅(一): 泛型 和IComparable、IComparer
    使用HttpWebRequest来秒杀
    NameValueCollection Dictionary区别
    在C#中使用代理的方式触发事件 (委托和事件 )(二)(转)
    SQL2005语句实现行转列,列转行
    值类型和引用类型的区别?(转)
    2010年年终总结
  • 原文地址:https://www.cnblogs.com/hzoi-mafia/p/7617708.html
Copyright © 2011-2022 走看看