【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.

你可能感兴趣的:(2008)