「网络流」
网络流专题还有3个题,就暂时告一段落了。
分类
最大流,费用流,可行流,上下界 有/无源汇 最大/最小/可行 void/费用 流。
写在前面
本blog例题顺序与难度无关,与校内OJ的顺序挂钩。
开始
我接触的网络流里模板大概有isap,dinic,EK,zkw,其实模板很简单,建边才是重点
奇怪的游戏
奇偶染色是基础,我们并不能发现,假设最后变成的数是x,奇格子的原数和sum1,数量a1,偶格子的原数和sum2,数量a2,那么一定满足a1*x-sum1=a2*x-sum2(增量相同),也就是(a1-a2)x=sum1-sum2,如果不是奇奇图(n,m都为奇)的话,a1!=a2,那如果能变成一个数,最后的x就一定是(sum1-sum2)/(a1-a2),但这个x一定要大于等于最大的数。如果不是这种情况,那么最后变成的数一定就具有二分的性质(因为奇格子=偶格子),那么我们二分最后变成的数,建图跑最大流看是否满流
建图方式:设二分数为mid,lnk(S,奇,mid-原值),lnk(偶,T,mid-原值),lnk(i,四周,inf)。按奇偶二分图,每个点向四周连边
士兵占领
“至少”这个条件对网络流很不友好,就转化一下条件,变成咩行咩列最多不放的士兵个数,求最多不放的士兵个数,就是网络流的基础型了
建图方式:二分图
紧急疏散evacuate
二分答案mid是基础,预处理每个空地经过正常的路线到每个门的时间,把每个门拆成mid个点,依次连边代表i时刻可以出去的人i+1时刻也可以出去,然后跑最大流
狼抓兔子
对偶图/最小割。我对对偶图的理解是由于你从S到T在外面连了一个边把外面分成了S‘和T’,那么划一下图就会发现每一条路(不仅仅是最短路)一定是原图的割,那么找到最短路就是最小割
切糕
对于题中给出的限制,需要用inf边处理,目的是“保证不会进行这种操作”,连inf边(a,b),意思是不能同时割S->b,a->T。满流的边可能有很多,但割边每一条链都是确定的
Figure Eight
最大获利
一个点的收益需要两个花费,代表这个点需要两个花费的前提,是个比较基础的最大权闭合子图。
可以建出点然后连inf边跑最小割
happiness
有个比较新的建点方式,S向点连文喜悦值,相邻点在新建一个点由S连入同时选文的喜悦值再连向两个相邻点inf,到T同理。这样就保证了当一方同时被割掉时就会因为没有了流不会割掉另一方的相同是的喜悦值,画一下几个情况就会发现它是对的
employ人员雇佣
题意比较新奇,就是如果你不选它并且你选了其它人的话他还会给你造成负贡献,这时推荐使用一种只考虑二元组建边的方法,通过考虑a,b都选,都不选,选a不选b,选b不选a四种情况的正确割的流量来建出二元组的边,这里没有图可以去网上搜搜二元组建边
不同的最小割
最小割树的板子,思路好理解,代码好背,我不会证
晨跑
一个比较基础的拆点方式,把点拆两个中间连流量为1的边表示这个点只能经过一次,然后跑最小费用最大流
80人环游世界
上下界网络流板子,或者把点拆两个中间连流量为Vi费用为-inf的边来确保这条边一定会被跑满。这种方法似乎还可以拓展解决一些类似的上下界的问题,就建一条-inf边一条普通边,我觉得亲兄弟说的是对的。
还有一种和星际竞速类似的做法,把每个点拆成入点和出点,由于保证点一定被经过Vi次,就是说一定会出去Vi次,进来Vi次,把S连向出点,入点连向T,流量都为Vi,再从点的出点连向其它点的入点,目的是占掉点的入度的一个名额,同时S连向新建SS流量为m费用为0,SS连向点的入点流量为inf费用为0目的也是占据点的入度,因为求的是最小费用最大流,最后入点的入度一定会被跑满,所以它是对的
修车
与普及组难度的节水问题不同在于每个师傅的修车时间不同,考虑一个师傅修的车可以看作是一条链,倒着想那么每辆车做的贡献是递增的,这样的话我们倒着考虑最后一辆车的贡献是1×费用,倒二是2×费用······把每个师傅拆成n个点,每个点向T连流量为1费用为0的边 ,每辆车向备份连流量为1,费用1×cost,2×cost······因为费用流会优先选择费用最小的,所以这样建边是正确的
数字配对
如果两个点可以配对,那么他们的质因数个数一定只差1,那么把所有点按照质因数个数奇偶建二分图,向S/T连流量为个数费用为0的边,再在配对两点连流量为inf费用为ci×cj的边,跑最小费用最大流一直到费用<0
美食节
与修车很类似,不同的是nmq的边数不能过了,这时候考虑优化修车的建图,显然最后其实只会用到p个拆点后的厨师,那么大可不必把每个厨师都拆成p个,可以动态拆点,也就是用哪一个厨师再新建一个他的点
n和m不要打反,调了一晚上
无限之环
是先考了类似的题才来打这道题的,感觉很不爽...
还是考虑循环流无法处理,因为黑白点的流量一定相等,就考虑由黑点流流量白点接住。
分几种水管的情况,旋转就相当于对应方向向对应方向连边,在保证最大流(即所有方向的流都有白点接住的情况下)要求最小费用。
1 #include<bits/stdc++.h> 2 const int N=20000,M=560000,inf=0x3f3f3f3f; 3 using namespace std; 4 int n,m,S,T,B,minspend,head[N],to[M],nxt[M],flo[M],spe[M],dis[N],inq[N]; 5 inline int rd(){ 6 register int x=0,f=1;char ch=getchar(); 7 while(!isdigit(ch)) f=ch=='-'?-1:1,ch=getchar(); 8 while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-48,ch=getchar(); 9 return x*f; 10 } 11 inline void lnk(int x,int y,int flow,int spend){ 12 to[++B]=y,nxt[B]=head[x],head[x]=B,flo[B]=flow,spe[B]=spend; 13 to[++B]=x,nxt[B]=head[y],head[y]=B,flo[B]=0,spe[B]=-spend; 14 } 15 int JI; 16 inline void Lnk(int x,int y,int flow,int spend){ 17 JI?lnk(x,y,flow,spend):lnk(y,x,flow,spend); 18 } 19 inline int id(int x,int y,int cpy){ 20 return cpy*n*m+(x-1)*m+y; 21 } 22 inline bool spfa(){ 23 memset(dis,0x3f,sizeof dis); dis[S]=0; 24 queue <int> q; q.push(S); inq[S]=1; 25 while(!q.empty()){ 26 int u=q.front(); q.pop(); inq[u]=0; 27 for(int i=head[u];i;i=nxt[i]) if(dis[to[i]]>dis[u]+spe[i]&&flo[i]){ 28 dis[to[i]]=dis[u]+spe[i]; 29 if(!inq[to[i]]) q.push(to[i]),inq[to[i]]=1; 30 } 31 } 32 return dis[T]!=inf; 33 } 34 int dinic(int u,int cup){ 35 if(u==T) return cup; 36 int res=cup,v; inq[u]=1; 37 for(int i=head[u];i;i=nxt[i]) if(dis[to[i]]==dis[u]+spe[i]&&flo[i]&&!inq[to[i]]){ 38 v=dinic(to[i],min(res,flo[i])); 39 if(!v) dis[to[i]]=-1; 40 else res-=v,flo[i]-=v,flo[i^1]+=v,minspend+=v*spe[i]; 41 if(!res) break; 42 } 43 inq[u]=0; 44 return cup-res; 45 } 46 int main(){ 47 n=rd(); m=rd(); S=n*m*5+1; T=S+1; B=1; int cnt=0; 48 for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ 49 int x=rd();JI=(i+j)&1; 50 int W=(x&1)+((x>>1)&1)+((x>>2)&1)+((x>>3)&1); 51 if(x&1) Lnk(id(i,j,0),id(i,j,2),1,0); 52 if(x&2) Lnk(id(i,j,0),id(i,j,3),1,0); 53 if(x&4) Lnk(id(i,j,0),id(i,j,4),1,0); 54 if(x&8) Lnk(id(i,j,0),id(i,j,1),1,0); 55 if(JI){ 56 cnt+=W; 57 Lnk(S,id(i,j,0),W,0);//printf("W:%d\n",W); 58 if(j-1) Lnk(id(i,j,1),id(i,j-1,3),1,0); 59 if(i-1) Lnk(id(i,j,2),id(i-1,j,4),1,0); 60 if(j+1<=m) Lnk(id(i,j,3),id(i,j+1,1),1,0); 61 if(i+1<=n) Lnk(id(i,j,4),id(i+1,j,2),1,0); 62 } 63 else Lnk(T,id(i,j,0),W,0); 64 65 int id1=id(i,j,1),id2=id(i,j,2),id3=id(i,j,3),id4=id(i,j,4); 66 switch(x){ 67 case 0:break;// 68 case 5:break;// 69 case 10:break;// 70 case 15:break;// 71 72 case 1:Lnk(id2,id1,1,1),Lnk(id2,id3,1,1),Lnk(id2,id4,1,2);break; 73 case 2:Lnk(id3,id2,1,1),Lnk(id3,id4,1,1),Lnk(id3,id1,1,2);break; 74 case 4:Lnk(id4,id1,1,1),Lnk(id4,id3,1,1),Lnk(id4,id2,1,2);break; 75 case 8:Lnk(id1,id2,1,1),Lnk(id1,id4,1,1),Lnk(id1,id3,1,2);break; 76 77 case 3:Lnk(id3,id1,1,1),Lnk(id2,id4,1,1);break; 78 case 6:Lnk(id3,id1,1,1),Lnk(id4,id2,1,1);break; 79 case 9:Lnk(id1,id3,1,1),Lnk(id2,id4,1,1);break; 80 case 12:Lnk(id1,id3,1,1),Lnk(id4,id2,1,1);break; 81 82 case 7:Lnk(id2,id1,1,1),Lnk(id4,id1,1,1),Lnk(id3,id1,1,2);break; 83 case 11:Lnk(id1,id4,1,1),Lnk(id3,id4,1,1),Lnk(id2,id4,1,2);break; 84 case 13:Lnk(id2,id3,1,1),Lnk(id4,id3,1,1),Lnk(id1,id3,1,2);break; 85 case 14:Lnk(id1,id2,1,1),Lnk(id3,id2,1,1),Lnk(id4,id2,1,2);break; 86 } 87 } 88 int maxflow=0; 89 while(spfa()) maxflow+=dinic(S,inf); 90 // printf("%d %d\n",maxflow,cnt); 91 printf("%d\n",maxflow!=cnt?-1:minspend); 92 return 0; 93 }
其实好像有点不严谨,能hack的数据应该是黑白点本来流量就不等的情况吧(比如只有四个点,其中3个两个水管,还有一个三个水管但是是白点就把黑点都跑满流了),不过数据好像没有这样的?(或者我在口胡)
星际竞速
个人觉得上下界网络流l=1,r=1也能做的酱紫
把每个点拆成入点和出点,由于保证点一定被经过1次,就是说一定会出去1次,进来1次,把S连向出点,入点连向T,流量都为1,再从点的出点连向其它点的入点,目的是占掉点的入度的一个名额,同时S连向新建SS流量为1费用为0,SS连向点的入点流量为inf费用为星际跳跃的费用目的也是占据点的入度,同时出点再连向SS流量为1费用为0因为求的是最小费用最大流,最后入点的入度一定会被跑满,所以它是对的
问题:有没有可能起始边不流,即S连向新建SS流量为1费用为0的边没有流量呢?
回答:有可能。只是此时这条边的流量就转移到了另外的星际跳跃的边上,本质不变,因为只能由每个星球飞往引力比它大的星球,一定有点通过星际跳跃跳到的,随便选择一个就是起点了,删掉这条边也是对的
还有一种比较亲兄弟的做法。点拆成入点和出点,从S连向入点流量为1费用为星际跳跃的费用,从入点连高速航行的边到其他出点,出点连向T流量为1费用为0,出点连向自己入点流量为1/[1,1]费用为-inf/0,这种做法里每一个流量代表的就不是一个人的旅行而是以一次星际跳跃为开始无限次高速航行最终准备下一次的星际跳跃,跑最小费用可行流/上下界最小费用可行流。
问题:为啥不是最大流,亲兄弟求解释?
回答:考虑流量的含义是以一次星际跳跃为开始无限次高速航行最终准备下一次的星际跳跃的次数,如果要最大流那就必须进行n次星际跳跃
千钧一发
继续考虑正难则反,或者叫sum-最小割的思路转化,题目转化为求不能同时选择的值的最小值
i和j建边条件是存在$a^2+b^2=t^2$并且$a$和$b$互质,根据条件贰一定是奇偶,奇奇间建边,再根据第一个条件一定是奇偶间建边。
因为两个奇数相加一定是偶数,偶数如果是完全平方数就一定是4的倍数,即膜4为0,而奇数%4只可能为1或3,平方后%4只可能为1,因此两个1相加不可能。
想到了这个后就可以分奇偶建二分图跑最小割了
老C的方块
线性代数
把式子划到底就是$-\sum \limits_{i=1}^{n}A_i*B_i+ \sum \limits_{i=1}^{n} \sum \limits_{j=1}^{n} A_i*A_j*C_{i,j}$
似乎就成了最大获利/人员雇佣? sum-最小割
海拔
自己思考可以发现一定海拔只有0,1两种情况,然后只有边界处有花费,问题变成了求最小割,但网络流会T90。
还记得狼抓兔子吗?平面图转对偶图跑最短路就行了
植物大战僵尸
最大权闭合子图的标志是每个点有一些限制表示”选我必须先选它们“。每一行i向i+1连边表示限制,点向保护它的点连边表示限制,注意了如果出环的话环和被环保护的点都不能选,我们发现这恰好是拓扑排序,只不过用流量的边的反向边是对的,想想就懂了,或者用一种膜改版的tarjan对每个点的好坏情况进行|操作,建好内部inf图了正连S负连T跑最小割
寿司餐厅
最大权闭合子图的标志是每个点有一些限制表示”选我必须先选它们“。区间[l,r]的寿司连向[l,r-1],[l+1,r]表示限制,单点寿司连向T流量为x,相同标号的点连向同一个新建点”?“流量为inf,”?“连向T流量为$mx^2$,其中的含义大概想想就能明白,需要重点理解的是”限制“的含义,建好内部inf图了正连S负连T跑最小割
集合交易
矩阵
一共三道二分题一道也没有独立想出来,我怕是noip不扎实啊。最大值最小用二分,那个式子的含义是使每行每列的B和与A和差绝对值中最大的最小,二分最大值,那么max(0,A-mid)=<B<=A+mid,按行列建二分图行列间连流量为[L,R]的边,S向行连流量为[max(0,A-mid),A+mid]的边,列向T连流量为[max(0,A-mid),A+mid]的边,跑有源汇上下界可行流判断是否有解。需要注意的是上下界网络流里可行流的含义是”满足了所有边的条件“,即合法。类似于二分里check操作。有可行流就代表着check(true)
支线剧情
有源汇上下界最小费用可行流。
问题:为啥不是最大/最小流?
回答:因为开始建边保证不会出现负环,如果再出现一条费用为负的通路,由于(t,s,inf,0)边的存在就会出现负环,而开始没有负环,费用用不会改变,因此最后一定不会出现负环
志愿者招募
一道大神题
那条(ti+1,s,0,inf,cost)的反向边我还是不能简洁的理解,划下图可以明白一些
旅行时的困惑
树dp/上下界有源汇最小可行流
清理雪道
上下界有源汇最小可行流。
最小流介绍两种方法。
一种是建出t到s的inf流后先跑S与T的最大流记录此时t到s的inf的反向边的流量就是原图可行流的流量,删掉t到s的inf边从t到s跑最大流尽可能的退流用刚才t到s的inf的反向边的流量减此时的最大流就是最小流
一种是直接跑S与T的最大流,再建出t到s的inf边再跑一次S与T的最大流,此时t到s的inf的反向边的流量就是原图的最小流。理解方式是因为t到s的inf的反向边的流量就是原图的流量,所以先让原图能替代它跑的流量都跑满,剩下的再跑t到s的inf的反向边就可以使得t到s的inf的反向边的流量最小咯
最大流介绍两种方法。
一种是建出t到s的inf流后先跑S与T的最大流记录此时t到s的inf的反向边的流量就是原图可行流的流量,删掉t到s的inf边从s到t跑最大流用刚才t到s的inf的反向边的流量加此时的最大流就是最大流
一种是直接跑S与T的最大流再直接跑一遍s与t的最大流就是最大流。理解方式是t到s的边不会走(一到t就return了),t到s的反向边的流量就是上一次原图可行流的流量,直接就会跑满,相当于是直接加上去了