zoukankan      html  css  js  c++  java
  • 【NOI 2008】志愿者招募 Employee

    [Noi2008]志愿者招募

    Time Limit:20000MS  Memory Limit:165536K
    Total Submit:262 Accepted:162
    Case Time Limit:2000MS

    Description

    申奥成功后,布布经过不懈努力,终于成为奥组委下属公司人力资源部门的主管。布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者。经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人。
    布布通过了解得知,一共有M 类志愿者可以招募。其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元。新官上任三把火,为了出色地完成自己的工作,布布希望用尽量少的费用招募足够的志愿者,但这并不是他的特长!于是布布找到了你,希望你帮他设计一种最 优的招募方案。

    Input

    第一行包含两个整数N, M,表示完成项目的天数和可以招募的志愿者的种类。
    接下来的一行中包含N 个非负整数,表示每天至少需要的志愿者人数。
    接下来的M 行中每行包含三个整数Si, Ti, Ci,含义如上文所述。为了方便起见,我们可以认为每类志愿者的数量都是无限多的。

    Output

    仅包含一个整数,表示你所设计的最优方案的总费用。

    Sample Input

    3 3
    2 3 4
    1 2 2
    2 3 5
    3 3 2

    Sample Output

    14

    Hint

    招募第一类志愿者3名,第三类志愿者4名
    30%的数据中,1 ≤ N, M ≤ 10,1 ≤ Ai ≤ 10;
    100%的数据中,1 ≤ N ≤ 1000,1 ≤ M ≤ 10000,题目中其他所涉及的数据均
    不超过2^31-1。
    

      这两天做题有点杂,POJ、SGU、APIO都是看到自己什么地方弱做什么……

      此题一看就是费用流……明显的,可以把每一天看成是一个节点,然后跑最小费用最大流……但是构图挺难的。

      首先需要明确一个概念:每一天的开始和结束,他们不是一个点。第一天的结束等价于第二天的开始,以此类推……所以,n天一共就是n+1个点。

      然后我们设立source和sink。对于一天的开始(就是1..n),与源连边,流量为当天所需人数(a[i]),费用为0;对于每一天的结束(就是第二天的开始,2..n+1),与汇连一条边,流量为当天所需人数(注意是a[i-1]!),费用为0。这样我们可以满足每一天的人数需求。

      对于每个种类的志愿者,将起始日(s)的开始于结束日的结束(t+1)连一条边,流量为INF(因为志愿者数量是无限的),费用为招募费用。

      然后对于每一天的结束,与这一天的开始连一条容量为INF,费用为0的边,这样可以满足招募志愿者花费的费用只统计一次。

      网络模型建好之后,就可以直接跑费用流了……

      Clavichord非说SPFA会TLE……然后我就很淡定地写SPFA……AC了……

    program NOI_2008_Employee;
    type rec=record
                   s,endv,next,w,c,pre:longint;
    end;
    var edge:array[1..50000]of rec;
        pre,w,father:Array[0..10002]of longint;
        q:array[1..1000000]of longint;
        vis:array[0..10002]of boolean;
        dist:Array[0..10002]of longint;
        n,m,source,sink,ans,total:longint;
    
    procedure addedge(s,t,w,c:longint);
    begin
      inc(total);
      edge[total].s:=s;
      edge[total].endv:=t;
      edge[total].w:=w;
      edge[total].c:=c;
      edge[total].next:=father[s];
      father[s]:=total;
      edge[total].pre:=total+1;
    
      inc(total);
      edge[total].s:=t;
      edge[total].endv:=s;
      edge[total].w:=0;
      edge[total].c:=-c;
      edge[total].next:=father[t];
      father[t]:=total;
      edge[total].pre:=total-1;
    end;
    
    function cmp(a,b:longint):longint;
    begin
      if a<b then exit(a);
      exit(b);
    end;
    
    procedure init;
    var i,a,b,c:longint;
    begin
      fillchar(father,sizeof(father),$ff);
      readln(n,m);
      source:=0;sink:=n+2;
      for i:=1 to n do read(w[i]);
      for i:=1 to n do addedge(source,i,w[i],0);
      for i:=2 to n+1 do addedge(i,sink,w[i-1],0);
      for i:=1 to m do
        begin
          readln(a,b,c);
          addedge(a,b+1,maxlongint,c);
        end;
      for i:=2 to n+1 do addedge(i,i-1,maxlongint,0);
    end;
    
    function spfa:boolean;
    var head,tail,now,p:longint;
    begin
      fillchar(vis,sizeof(vis),false);
      fillchar(pre,sizeof(pre),0);
      head:=1;tail:=1;
      q[head]:=0;
      fillchar(dist,sizeof(dist),$7f);
      dist[0]:=0;
      vis[0]:=true;
      while head<=tail do
        begin
          now:=q[head];
          p:=father[now];
          while p>0 do
            begin
              if (edge[p].w>0)and(dist[edge[p].endv]>dist[now]+edge[p].c)then
                begin
                  dist[edge[p].endv]:=dist[now]+edge[p].c;
                  pre[edge[p].endv]:=p;
                  if not vis[edge[p].endv] then
                    begin
                     vis[edge[p].endv]:=true;
                     inc(tail);
                     q[tail]:=edge[p].endv;
                    end;
                end;
              p:=edge[p].next;
            end;
          vis[now]:=false;
          inc(head);
        end;
      exit(dist[sink]<1000000);
    end;
    
    procedure augument;
    var min,p:longint;
    begin
      while spfa do
        begin
          p:=pre[sink];
          min:=maxlongint;
          while edge[p].s<>source do
            begin
              min:=cmp(edge[p].w,min);
              p:=pre[edge[p].s];
            end;
          min:=cmp(edge[p].w,min);
          p:=pre[sink];
          while edge[p].s<>source do
            begin
              dec(edge[p].w,min);
              inc(edge[edge[p].pre].w,min);
              p:=pre[edge[p].s];
            end;
          dec(edge[p].w,min);
          inc(ans,dist[sink]*min);
        end
    end;
    
    begin
      assign(input,'employee.in');reset(input);
      assign(output,'employee.out');rewrite(output);
      init;
      augument;
      writeln(ans);
      readln;readln;
      close(output);
    end.
    
  • 相关阅读:
    python删除列表重复元素
    maven常用打包命令
    python开发之函数
    手把手教你用Strace诊断问题
    python引用列表--10
    Python中open函数怎么操作文件--9
    python数据操作--8
    图解源码之FutureTask篇(AQS应用)
    图解源码之java锁的获取和释放(AQS)篇
    图解线程池工作机制,手写线程池?
  • 原文地址:https://www.cnblogs.com/Delostik/p/1992216.html
Copyright © 2011-2022 走看看