思考一下我们接触的最小割问题
-
最小割的基本问题(可能会和图论的知识相结合,比如bzoj1266,bzoj1797)
-
最大权闭合图(bzoj1497)
-
最大点权覆盖集,最大点权独立集(bzoj1324)
最近接触到了一类关于最小割新的问题,我也不知道叫什么好
反正它有这么几个特点
-
每个点都有两种选择的可能性,设为属于S和属于T,属于S有收益a[i],属于T有收益b[i]
-
两点之间可能因为同在一个或不在同一个集合内产生额外的收益
-
使收益最大化(废话)
大概就是这个意思,如果看不懂也没关系,我们先从最简单的问题开始分析
首先在满足1的条件下,我们有m对关系,点i和j若同在一个集合,那么就能产生一个额外收益w[i,j];
最终使收益最大化。
对此,我们对于每个点i,我们连s--->i 流量为a[i], i--->t 流量为b[i];
然后对于每对关系(i,j),连i<---j,j--->i 流量都为w[i,j];
然后怎么做呢?根据割的定义,就是s到t没有路
在这里就是,割完之后每个点要么和s连,要么和t连;
我们假如以两个点的图为例,思考一下最小割可能割掉的边(我比较懒,就不发图了)
就会发现,这里的最小割正能使不在一个集合的两个点之间的边被切断
或者使在一个集合的两个点之间的边不被切断
最终ans=∑a[i]+∑b[i]+∑w[i,j]-mincut
这里割对应一种解决问题的方案
好这是最初步,下面就是bzoj2132的问题,
条件2变成了,不在同一个集合里的两点会产生一个额外的收益
我们先按基本问题的方式建图
有了最开始的经验,我们不难猜测,
假如选择一个点集,使其中的点i,颠倒一下使s-->i的流量为b[i],i-->t的流量为a[i];
这样我们再做最小割,不就又变回了最基本的问题吗?
而我们应该怎么选择点集呢?
显然,这个点集内部的点是不能有关系的,并且,这些点是给出关系中一侧的点;
说的明确一点,就是不管S,T,根据关系建得的图G,必须是一个二分图;
那么这道题满不满足呢?
我们对平面图黑白相间染色,显然黑点与白点才会产生关系,
黑点与黑点,白点与白点之间是没有关系——是二分图!
于是这道题就简单了!
1 const dx:array[1..4] of integer=(0,0,1,-1); 2 dy:array[1..4] of integer=(1,-1,0,0); 3 inf=100000007; 4 5 type node=record 6 point,next,flow:longint; 7 end; 8 9 var edge:array[0..200010] of node; 10 p,cur,h,numh,pre:array[0..10010] of longint; 11 c,num:array[0..110,0..110] of longint; 12 s,x,y,i,j,k,n,m,len,t:longint; 13 14 procedure add(x,y,f:longint); 15 begin 16 inc(len); 17 edge[len].flow:=f; 18 edge[len].point:=y; 19 edge[len].next:=p[x]; 20 p[x]:=len; 21 end; 22 23 begin 24 len:=-1; 25 fillchar(p,sizeof(p),255); 26 readln(n,m); 27 t:=n*m+1; 28 for i:=1 to n do 29 for j:=1 to m do 30 begin 31 inc(k); 32 num[i,j]:=k; 33 read(x); 34 s:=s+x; 35 if (i+j) mod 2=1 then //黑白染色 36 begin 37 add(0,k,x); 38 add(k,0,x); 39 end 40 else begin 41 add(k,t,x); 42 add(t,k,0); 43 end; 44 end; 45 46 for i:=1 to n do 47 for j:=1 to m do 48 begin 49 read(x); 50 s:=s+x; 51 if (i+j) mod 2=1 then 52 begin 53 add(num[i,j],t,x); 54 add(t,num[i,j],x); 55 end 56 else begin 57 add(0,num[i,j],x); 58 add(num[i,j],0,0); 59 end; 60 end; 61 for i:=1 to n do 62 for j:=1 to m do 63 read(c[i,j]); 64 for i:=1 to n do 65 for j:=1 to m do 66 for k:=1 to 4 do 67 begin 68 x:=i+dx[k]; 69 y:=j+dy[k]; 70 if num[x,y]>0 then 71 begin 72 s:=s+c[i,j]; 73 add(num[i,j],num[x,y],c[i,j]+c[x,y]); //两地建筑物类型不同,增加的额外收益是相互的,一荣俱荣一损俱损 74 add(num[x,y],num[i,j],0); 75 end; 76 end; 77 writeln(s-sap); //求最大流省略 78 end.
下面再扩展这个问题
条件2变为若点i,j同属于S,增加额外收益为w[i,j],
若点i,j同属于T,增加额外收益为r[i,j]; (这就是bzoj2127)
我们对于每个点i,我们连s--->i 流量为a[i], i--->t 流量为b[i];
显然,之前根据关系的建图方法已经不适用了,我们要思考怎么解决同属于S,同属于T所带来的收益
这必须得回到割的意义上来,我们知道割表示损失,我们取不到的
那我们就来分析一下损失:
1. 当i和j同时属于S的时候,我们损失了r[i,j]
2. 当i和j同时属于T的时候,我们损失了w[i,j]
3. 当i,j属于不同的集合,我们损失了w[i,j]+r[i,j]
这有什么用呢?我们可以换一种方式表达
当i属于S时,损失了r[i,j]/2,当j属于S时,损失了r[i,j]/2
当i属于T时,损失了w[i,j]/2,当j属于T时,损失了w[i,j]/2
这样我们再看3,我们还要在增加(w[i,j]+r[i,j])/2的损失
不难发现,问题又变回了最基本的问题
对于点i,我们增加s--->i 流量为r[i,j]/2,t--->i 流量为w[i,j]/2
对于每对关系(i,j),连i<---j,j--->i 流量都为(w[i,j]+r[i,j])/2;
ans=所有可能收益和-mincut
为了方便,我们可以一开始把流量全乘2,最后再除以2即可
友情提示:这道题不加当前弧优化的sap会TLE
1 const inf=1000000007; 2 dx:array[1..4] of integer=(-1,1,0,0); 3 dy:array[1..4] of integer=(0,0,-1,1); 4 5 type node=record 6 point,next,flow:longint; 7 end; 8 9 var edge:array[0..500010] of node; 10 d,p,cur,h,numh,pre:array[0..10010] of longint; 11 a:array[0..110,0..110,1..4] of longint; 12 num,b,c:array[0..110,0..110] of longint; 13 n,m,t,s,x,i,j,k,len,y:longint; 14 15 function min(a,b:longint):longint; 16 begin 17 if a>b then exit(b) else exit(a); 18 end; 19 20 procedure add(x,y,f:longint); 21 begin 22 inc(len); 23 edge[len].point:=y; 24 edge[len].flow:=f; 25 edge[len].next:=p[x]; 26 p[x]:=len; 27 end; 28 29 function sap:longint; 30 var tmp,neck,u,i,j,q:longint; 31 flag:boolean; 32 33 begin 34 numh[0]:=t+1; 35 u:=0; 36 sap:=0; 37 cur:=p; 38 neck:=inf; 39 while h[0]<t do 40 begin 41 d[u]:=neck; 42 flag:=false; 43 i:=cur[u]; 44 while i<>-1 do 45 begin 46 j:=edge[i].point; 47 if (edge[i].flow>0) and (h[u]=h[j]+1) then 48 begin 49 flag:=true; 50 neck:=min(neck,edge[i].flow); 51 cur[u]:=i; 52 pre[j]:=u; 53 u:=j; 54 if u=t then 55 begin 56 sap:=sap+neck; 57 i:=t; 58 while i<>0 do 59 begin 60 i:=pre[i]; 61 j:=cur[i]; 62 dec(edge[j].flow,neck); 63 inc(edge[j xor 1].flow,neck); 64 end; 65 neck:=inf; 66 u:=0; 67 end; 68 break; 69 end; 70 i:=edge[i].next; 71 end; 72 if not flag then 73 begin 74 dec(numh[h[u]]); 75 if numh[h[u]]=0 then exit; 76 tmp:=t; 77 i:=p[u]; 78 q:=0; 79 while i<>-1 do 80 begin 81 j:=edge[i].point; 82 if (edge[i].flow>0) and (h[j]<tmp) then 83 begin 84 q:=i; 85 tmp:=h[j]; 86 end; 87 i:=edge[i].next; 88 end; 89 cur[u]:=q; 90 h[u]:=tmp+1; 91 inc(numh[h[u]]); 92 if u<>0 then 93 begin 94 u:=pre[u]; 95 neck:=d[u]; 96 end; 97 end; 98 end; 99 end; 100 101 begin 102 readln(n,m); 103 len:=-1; 104 fillchar(p,sizeof(p),255); 105 t:=n*m+1; 106 for i:=1 to n do 107 begin 108 for j:=1 to m do 109 begin 110 inc(k); 111 num[i,j]:=k; 112 read(x); 113 s:=s+x; 114 x:=x shl 1; 115 b[i,j]:=b[i,j]+x; 116 end; 117 readln; 118 end; 119 for i:=1 to n do 120 begin 121 for j:=1 to m do 122 begin 123 read(x); 124 s:=s+x; 125 x:=x shl 1; 126 c[i,j]:=c[i,j]+x; 127 end; 128 readln; 129 end; 130 131 for i:=1 to n-1 do 132 begin 133 for j:=1 to m do 134 begin 135 read(x); 136 s:=s+x; 137 b[i,j]:=b[i,j]+x; 138 b[i+1,j]:=b[i+1,j]+x; 139 a[i,j,2]:=a[i,j,2]+x; 140 a[i+1,j,1]:=a[i+1,j,1]+x; 141 end; 142 readln; 143 end; 144 145 for i:=1 to n-1 do 146 begin 147 for j:=1 to m do 148 begin 149 read(x); 150 s:=s+x; 151 c[i,j]:=c[i,j]+x; 152 c[i+1,j]:=c[i+1,j]+x; 153 a[i,j,2]:=a[i,j,2]+x; 154 a[i+1,j,1]:=a[i+1,j,1]+x; 155 end; 156 readln; 157 end; 158 159 for i:=1 to n do 160 begin 161 for j:=1 to m-1 do 162 begin 163 read(x); 164 s:=s+x; 165 b[i,j]:=b[i,j]+x; 166 b[i,j+1]:=b[i,j+1]+x; 167 a[i,j,4]:=a[i,j,4]+x; 168 a[i,j+1,3]:=a[i,j+1,3]+x; 169 end; 170 readln; 171 end; 172 173 for i:=1 to n do 174 begin 175 for j:=1 to m-1 do 176 begin 177 read(x); 178 s:=s+x; 179 c[i,j]:=c[i,j]+x; 180 c[i,j+1]:=c[i,j+1]+x; 181 a[i,j,4]:=a[i,j,4]+x; 182 a[i,j+1,3]:=a[i,j+1,3]+x; 183 end; 184 readln; 185 end; 186 for i:=1 to n do 187 for j:=1 to m do 188 begin 189 for k:=1 to 4 do 190 begin 191 x:=i+dx[k]; 192 y:=j+dy[k]; 193 if num[x,y]>0 then 194 begin 195 add(num[i,j],num[x,y],a[i,j,k]); //注意序号的对应 196 add(num[x,y],num[i,j],0); 197 end; 198 end; 199 add(0,num[i,j],b[i,j]); 200 add(num[i,j],0,0); 201 add(num[i,j],t,c[i,j]); 202 add(t,num[i,j],0); 203 end; 204 writeln(s-sap div 2); 205 end.
这个问题最终阶段是bzoj3438 不再是两点间关系而是多点间关系了
即多个点如果同属A集合会产生一个额外收益c1,同属于B集合会产生一个额外收益c2
这就不能用上述的做法了,具体做法见bzoj3438的解题报告