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

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

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

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

但是

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

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

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

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

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

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

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

这就是AC方法

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

实现起来要小心

  1 const mo=4010;
  2 type node=record
  3        next:longint;
  4        point,flow,cost:integer;
  5      end;
  6 
  7 var edge:array[0..4100000] of node;
  8     v:array[0..4010] of boolean;
  9     d,rd,pre,pref:array[0..4010] of integer;
 10     q:array[0..4010] of integer;
 11     p,x,y,cur:array[0..4010] of longint;
 12     len:longint;
 13     max,s,t,i,j,n,m,be:integer;
 14 
 15 procedure add(x,y,f,c:integer);
 16   begin
 17     edge[len].point:=y;
 18     edge[len].flow:=f;
 19     edge[len].cost:=c;
 20     edge[len].next:=p[x];
 21     p[x]:=len;
 22   end;
 23 
 24 procedure preflow;
 25   var x,y,f,r,i,j:longint;
 26   begin
 27     r:=1;
 28     f:=1;
 29     q[1]:=s;
 30     while f<=r do  //拓扑排序
 31     begin
 32       x:=q[f];
 33       i:=p[x];
 34       while i<>-1 do
 35       begin
 36         y:=edge[i].point;
 37         dec(rd[y]);
 38         if (rd[y]=0) then
 39         begin
 40           inc(r);
 41           q[r]:=y;
 42         end;
 43         i:=edge[i].next;
 44       end;
 45       inc(f);
 46     end;
 47     fillchar(d,sizeof(d),255);
 48     d[s]:=0;
 49     for j:=1 to r do //DAG图根据拓扑的顺序dp解决最大路径
 50     begin
 51       x:=q[j];
 52       i:=p[x];
 53       while i<>-1 do
 54       begin
 55         y:=edge[i].point;
 56         if d[y]<d[x]+1 then
 57         begin
 58           d[y]:=d[x]+1;
 59           pre[y]:=x;
 60           cur[y]:=i;
 61           if d[y]>d[max] then max:=y;
 62         end;
 63         i:=edge[i].next;
 64       end;
 65     end;
 66   end;
 67 
 68 procedure change;   //建立预处理后的网络继续增广
 69   var i,j,e,w:longint;
 70   begin
 71     for i:=1 to n do
 72       pref[i]:=1;
 73     i:=max;
 74     while i<>s do   //相当于找到第一条增广路然后弄一弄反向弧什么的
 75     begin
 76       pref[i]:=0;
 77       j:=cur[i];
 78       dec(edge[j].flow);
 79       if pre[i]=s then w:=i;
 80       i:=pre[i];
 81     end;
 82     len:=-1;
 83     for i:=1 to n do
 84       for j:=1 to n do
 85         if (i<>j) then
 86           if (x[i]<=x[j]) and (y[i]<=y[j]) then
 87           begin
 88             inc(len);
 89             edge[len].point:=j+n;
 90             inc(len);
 91             add(j+n,i,1-edge[len-1].flow,0)  //添加原来没加的反向弧
 92           end;
 93 
 94     p[s]:=-1;
 95     for i:=1 to n do   //下面很多处理的细节,不赘述,繁杂但不难想到
 96     begin
 97       if i=w then e:=0 else e:=1;
 98       inc(len);
 99       add(s,i+n,e,0);
100       inc(len);
101       add(i+n,s,1-e,0);
102     end;
103 //0 表示超级源点,s表示起点,t表示超级汇点
104     t:=2*n+2;
105     inc(len);
106     add(0,s,1,0);
107     inc(len);
108     add(s,0,1,0);
109     for i:=1 to n do   
110     begin
111       inc(len);
112       add(i+n,i,pref[i],1);
113       inc(len);
114       add(i,i+n,1-pref[i],-1);
115       if max=i then e:=0   
116       else e:=1;
117       inc(len);
118       add(i,t,e,0);
119       inc(len);
120       add(t,i,1-e,0);
121     end;
122   end;
123 
124 function spfa:boolean;
125   var f,r,x,y,i,j,head,tail:longint;
126   begin
127     fillchar(v,sizeof(v),false);
128     for i:=0 to t do
129       d[i]:=-mo;
130     d[0]:=0;
131     q[0]:=0;
132     f:=0;
133     r:=0;
134     head:=1;
135     tail:=1;
136     while head<=tail do  //缩空间的写法,好久没这么写了
137     begin
138       x:=q[f];
139       v[x]:=false;
140       i:=p[x];
141       while i<>-1 do
142       begin
143         y:=edge[i].point;
144         if edge[i].flow>0 then
145         begin
146           if d[x]+edge[i].cost>d[y] then
147           begin
148             d[y]:=edge[i].cost+d[x];
149             pre[y]:=x;
150             cur[y]:=i;
151             if not v[y] then
152             begin
153               v[y]:=true;
154               r:=(r+1) mod mo;
155               inc(tail);
156               q[r]:=y;
157             end;
158           end;
159         end;
160         i:=edge[i].next;
161       end;
162       f:=(f+1) mod mo;
163       inc(head);
164     end;
165     if d[t]=-mo then exit(false) else exit(true);
166   end;
167 
168 function mincost:longint;  //最大费用最大流
169   var i,j:longint;
170   begin
171     be:=d[max];
172     mincost:=be;   //预处理出来的费用
173     while spfa do
174     begin
175       mincost:=mincost+d[t];
176       i:=t;
177       while i<>0 do
178       begin
179         j:=cur[i];
180         dec(edge[j].flow);
181         inc(edge[j xor 1].flow);
182         i:=pre[i];
183       end;
184     end;
185   end;
186 
187 begin
188   len:=-2;
189   fillchar(p,sizeof(p),255);
190   readln(n);
191   for i:=1 to n do
192     readln(x[i],y[i]);
193   for i:=1 to n do
194     for j:=1 to n do
195       if (i<>j) then
196         if (x[i]<=x[j]) and (y[i]<=y[j]) then
197         begin
198           len:=len+2;
199           add(i,j,1,0);  //个人感觉这样建之后添加边更容易一点
200           inc(rd[j]);
201         end;
202 
203   s:=2*n+1;
204   for i:=1 to n do
205     if rd[i]=0 then
206     begin
207       len:=len+2;
208       add(s,i,1,0);
209       inc(rd[i]); 
210     end;
211 
212   preflow;
213   change;
214   writeln(mincost);
215 end.
View Code

 

posted on 2014-07-15 23:07  acphile  阅读(180)  评论(0编辑  收藏  举报