一开始我觉得这不是一个弱弱的费用流吗?
每个豆豆拆点,入点出点随便连连
由于肯定是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.