一开始我觉得这不是一个弱弱的费用流吗?

每个豆豆拆点,入点出点随便连连

由于肯定是DAG图,边权为正的最大费用肯定能增广出来

于是我们只要跑总流量为2的最大费用最大流不就行了吗

但是

这样会TLE,因为会出现稠密图,spfa跑稠密图太慢

既然这样,能不能换一个找增广路的方法呢?

最大路径更快的方法就是DAG图的拓扑排序+dp

但好像无法处理反向弧导致带来的环啊

那既然这样,反正总流量很小,

我们可以用DAG解决最大路径特有的dp找第一条增广路(因为这时候是肯定的DAG),跑出一个较大费用

然后再用spfa增广呢?这样就大大减小了时间复杂度

这就是AC方法

注意这道题空间卡的较紧,能开integer不要开longint

实现起来要小心

 const mo=;
type node=record
       next:longint;
       point,flow,cost:integer;
     end; var edge:array[..] of node;
    v:array[..] of boolean;
    d,rd,pre,pref:array[..] of integer;
    q:array[..] of integer;
    p,x,y,cur:array[..] of longint;
    len:longint;
    max,s,t,i,j,n,m,be:integer; procedure add(x,y,f,c:integer);
  begin
    edge[len].point:=y;
    edge[len].flow:=f;
    edge[len].cost:=c;
    edge[len].next:=p[x];
    p[x]:=len;
  end; procedure preflow;
  var x,y,f,r,i,j:longint;
  begin
    r:=;
    f:=;
    q[]:=s;
    while f<=r do  //拓扑排序
    begin
      x:=q[f];
      i:=p[x];
      while i<>- do
      begin
        y:=edge[i].point;
        dec(rd[y]);
        if (rd[y]=) then
        begin
          inc(r);
          q[r]:=y;
        end;
        i:=edge[i].next;
      end;
      inc(f);
    end;
    fillchar(d,sizeof(d),);
    d[s]:=;
    for j:= to r do //DAG图根据拓扑的顺序dp解决最大路径
    begin
      x:=q[j];
      i:=p[x];
      while i<>- do
      begin
        y:=edge[i].point;
        if d[y]<d[x]+ then
        begin
          d[y]:=d[x]+;
          pre[y]:=x;
          cur[y]:=i;
          if d[y]>d[max] then max:=y;
        end;
        i:=edge[i].next;
      end;
    end;
  end; procedure change;   //建立预处理后的网络继续增广
  var i,j,e,w:longint;
  begin
    for i:= to n do
      pref[i]:=;
    i:=max;
    while i<>s do   //相当于找到第一条增广路然后弄一弄反向弧什么的
    begin
      pref[i]:=;
      j:=cur[i];
      dec(edge[j].flow);
      if pre[i]=s then w:=i;
      i:=pre[i];
    end;
    len:=-;
    for i:= to n do
      for j:= to n do
        if (i<>j) then
          if (x[i]<=x[j]) and (y[i]<=y[j]) then
          begin
            inc(len);
            edge[len].point:=j+n;
            inc(len);
            add(j+n,i,-edge[len-].flow,)  //添加原来没加的反向弧
          end;     p[s]:=-;
    for i:= to n do   //下面很多处理的细节,不赘述,繁杂但不难想到
    begin
      if i=w then e:= else e:=;
      inc(len);
      add(s,i+n,e,);
      inc(len);
      add(i+n,s,-e,);
    end;
// 表示超级源点,s表示起点,t表示超级汇点
    t:=*n+;
    inc(len);
    add(,s,,);
    inc(len);
    add(s,,,);
    for i:= to n do   
    begin
      inc(len);
      add(i+n,i,pref[i],);
      inc(len);
      add(i,i+n,-pref[i],-);
      if max=i then e:=   
      else e:=;
      inc(len);
      add(i,t,e,);
      inc(len);
      add(t,i,-e,);
    end;
  end; function spfa:boolean;
  var f,r,x,y,i,j,head,tail:longint;
  begin
    fillchar(v,sizeof(v),false);
    for i:= to t do
      d[i]:=-mo;
    d[]:=;
    q[]:=;
    f:=;
    r:=;
    head:=;
    tail:=;
    while head<=tail do  //缩空间的写法,好久没这么写了
    begin
      x:=q[f];
      v[x]:=false;
      i:=p[x];
      while i<>- do
      begin
        y:=edge[i].point;
        if edge[i].flow> then
        begin
          if d[x]+edge[i].cost>d[y] then
          begin
            d[y]:=edge[i].cost+d[x];
            pre[y]:=x;
            cur[y]:=i;
            if not v[y] then
            begin
              v[y]:=true;
              r:=(r+) mod mo;
              inc(tail);
              q[r]:=y;
            end;
          end;
        end;
        i:=edge[i].next;
      end;
      f:=(f+) mod mo;
      inc(head);
    end;
    if d[t]=-mo then exit(false) else exit(true);
  end; function mincost:longint;  //最大费用最大流
  var i,j:longint;
  begin
    be:=d[max];
    mincost:=be;   //预处理出来的费用
    while spfa do
    begin
      mincost:=mincost+d[t];
      i:=t;
      while i<> do
      begin
        j:=cur[i];
        dec(edge[j].flow);
        inc(edge[j xor ].flow);
        i:=pre[i];
      end;
    end;
  end; begin
  len:=-;
  fillchar(p,sizeof(p),);
  readln(n);
  for i:= to n do
    readln(x[i],y[i]);
  for i:= to n do
    for j:= to n do
      if (i<>j) then
        if (x[i]<=x[j]) and (y[i]<=y[j]) then
        begin
          len:=len+;
          add(i,j,,);  //个人感觉这样建之后添加边更容易一点
          inc(rd[j]);
        end;   s:=*n+;
  for i:= to n do
    if rd[i]= then
    begin
      len:=len+;
      add(s,i,,);
      inc(rd[i]); 
    end;   preflow;
  change;
  writeln(mincost);
end.

bzoj1930的更多相关文章

  1. 【BZOJ1930】[Shoi2003]pacman 吃豆豆 最大费用最大流

    [BZOJ1930][Shoi2003]pacman 吃豆豆 Description 两个PACMAN吃豆豆.一开始的时候,PACMAN都在坐标原点的左下方,豆豆都在右上方.PACMAN走到豆豆处就会 ...

  2. BZOJ1930 [Shoi2003]pacman 吃豆豆

     dp,首先建出图,f[i][j]表示a吃到了i点,b吃到了j点的最大值,转移的时候转移拓扑序小的那一维,如果i拓扑序小于j,那么转移到f[k][j],否则转移到f[i][k],建出的图边数也要优化, ...

  3. 【BZOJ1930】【SHOI2003】吃豆豆

    初见杀…… 原题: 两个PACMAN吃豆豆.一开始的时候,PACMAN都在坐标原点的左下方,豆豆都在右上方.PACMAN走到豆豆处就会吃掉它.PACMAN行走的路线很奇怪,只能向右走或者向上走,他们行 ...

  4. [转载]hzwer的bzoj题单

    counter: 664BZOJ1601 BZOJ1003 BZOJ1002 BZOJ1192 BZOJ1303 BZOJ1270 BZOJ3039 BZOJ1191 BZOJ1059 BZOJ120 ...

  5. BZOJ刷题列表【转载于hzwer】

    沿着黄学长的步伐~~ 红色为已刷,黑色为未刷,看我多久能搞完吧... Update on 7.26 :之前咕了好久...(足见博主的flag是多么emmm......)这几天开始会抽时间刷的,每天几道 ...

随机推荐

  1. GDI+中发生一般性错误之文件被占用

    有多种原因可能导致这个异常出现,比如创建文件的权限不足.文件被占用等. 这里提供一个使用Stream读取图片避免文件被占用的方法. public Image GetImageFromStream(st ...

  2. [Effective Java读书笔记] 第二章 创建和销毁对象(1~7)

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3537576.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...

  3. WinForm应用程序退出的方法

    this.Close(); 只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出. Application.Exit(); 强制所有消息中止,退出所有的 ...

  4. the evaluation period for visual studio trial edition has ended的解决方法-转发

    首先献上自己收集的Visual studio 2008序列号: Visual Studio 2008 Professional Edition: XMQ2Y-4T3V6-XJ48Y-D3K2V-6C4 ...

  5. php 一维数组排序,保留key值

    function sort_with_keyName($arr,$orderby='desc'){ //在内存的另一处 $a 复制内容与 $arr 一样的数组 foreach($arr as $key ...

  6. mysql show processlist 显示mysql查询进程

    1.进入mysql/bin目录下输入mysqladmin processlist; 2.启动mysql,输入show processlist; 如果有 SUPER 权限,则可以看到全部的线程,否则,只 ...

  7. Sql Server 判断表或数据库是否存在

    发布:thebaby   来源:脚本学堂     [大 中 小] 本文详细介绍了,在sql server中判断数据库或表是否存在的方法,有理论有实例,有需要的朋友可以参考下,一定有帮助的.原文地址:h ...

  8. PHP 向 MySql 中数据修改操作时,只对数字操作有效,非数字操作无效,怎么办?

    问题描述:   用PHP向MySql数据库中修改数据,实现增删改(数据库能正确连接) 经测试,代码只能对数字进行正常的增删改操作,非数字操作无效   但要在课程名称中输入中文,应该如果修改呢?   存 ...

  9. VC++2010添加菜单

    1  资源视图下面右键添加资源 选择menu 2  创建你想要的menu 3  找到CDialog::OnInitDialog();在后面添加代码. CMenu menu; menu.LoadMenu ...

  10. 021,lambda 表达式

    021,lambda 表达式  匿名函数: 快速定义单行的最小函数,是从lisp借用来的,可以用在任何需要函数的地方 >>> def ds(x):     return 2*x +  ...