网络流初步
已入土,没几道是自己想的。
个人认为主体思想是反悔贪心,做题关键是建图。
陈列一些颓过题解的大神题。
士兵占领:
转化题意,用总数减去最多的能丢掉的士兵数,变成最大流模型。
$addedge(St,zh(i,0),N-a[i]-L[i])$一行最多能减的士兵数
$addedge(zh(i,1),En,M-b[i]-C[i])$一列最多能减的士兵数
$addedge(zh(i,0),zh(j,1),1)$限制一个士兵只贡献$1$
紧急疏散evacuate:
拆点比较神,二分时间,把每个门拆出时间个点,$bfs$预处理最短路。
最大获利:
最小割模型。
最小割的定义是找到最小边权和的一组边使得St到En不联通。
应用在这里就是割掉这条边,相当于选择了对立的那条边。
$addedge(St,i,p),addedge(A,i+N,INF),addedge(B,i+N,INF),addedge(i+N,En,C)$
UPD:
貌似如果两个点同时选产生贡献用三叉戟,两个点不同时选产生的贡献将两个点相连即可。
不过是独立问题成立,杂成一起不知道是否可行。
二分图模型:
因为我是思维僵化的文化课选手,所以其实用网络流跑二分图最大匹配也不会。
二分图网络流建图:
1>如果模型是两种事物$(a,b)$的匹配:
$addedge(St,i,cap) i\in a$
$addedge(i,En,cap) i\in b$
$addedge(i,j,INF) i\in a ,j\in b$
2>否则,二分图染色把点分成两个集合再建边即可。
但这样表示的意义大概不太能理解成二分图的最大匹配,
在题目中大多以两个点不能同时选择的最小割出现。
(bzoj3158)
奇怪的游戏:棋盘模型和每次取的都是相邻的两个格,基本判断为二分图。在黑白点个数和权值和相同时,发现答案具有单调性,然后建二分图判断满流。
数字配对:通过$a_j|a_i$的判断条件可以发现一个性质,这个图不会出现环。简单口胡是因为边具有">"的传递性,然后构成整除关系一定有质因子指数和各为奇偶,所以可以根据指数和的奇偶分黑白点。(当然如果你不嫌麻烦,也可以预处理建图跑黑白染色)。
上下界网络流:
看了一下午博客,感觉稍有些入门。
核心是上下界可行流,可行流即存在一种满足条件的流。上下界的条件就是对于一些边规定了流量在$[l,r]$才算合法。
无源汇上下界可行流流程(即循环的红石电路):
1>钦定先给所有的边流满$cap=low$,把剩余流量转化成$r$。
2>因为流量不平衡,所以对于每个点统计一个当前的入流量和出流量之差$totflow$。
3>对于$totflow<0$的,$addedge(i,En,-totflow)$,反之$addedge(St,i,totflow)$
4>最大流如果跑慢证明有一组可行解,构造每条边的流量即$l+w$
至于3>为什莫不是$addedge(St,i,-totflow)$,$addedge(i,En,totflow)$,画个图即可:
如果某个点在所有边流量等于下界的初始流中满足流量守恒,那么这个点在附加流中也满足流量守恒,
如果某个点在初始流中的流入量比流出量多x,那么这个点在附加流中的流出量比流入量多x.
如果某个点在初始流中的流入量比流出量少x,那么这个点在附加流中的流出量比流入量少x.
-达哥
在附加流多出的流量转移给$En$,反之$St$提供缺少的流量,以此补流。
星际竞速:
拿到题仍然是铁憨憨。
题目是要求找到一条类似于一笔画的路线将所有点有且仅经过一次,求最小花费。
首先方向肯定是费用流。而‘所有点有且仅经过一次’大概是类似于<80人环游世界>中$[1,1]$的流量限制,开始想上下界。
这里思考出现了一个问题,正常的网络流,不管上下界也好,最大权闭合子图也罢,最后答案所构造的图形大概都是个标准的拓扑图,从$St$都会分出一些叉,但放在这道题里就不满足一条路走到头的要求啦。
怎样解决?考虑流的定义,他其实是一个上界限制,考虑到又是上下界,所以可以给原图的$st-md$($md$为中转站,图论的基本建图套路)的下界限制为$1$,上界限制也为$1$,也就是说只能增广一次,这使得最后跑出来不会分叉。但到此显然不够,还要保证这一个流不会只经过一个点就溜了,所以拆点建边$[1,1]$。因为只有一次增广,所以这条边的流一定要在这唯一一次的增广中满足,就能完成‘将所有点有且仅经过一次’的任务啦。
注意对于原图高速航行模式和能力爆发模式的边,下界限制为$0$,因为这条边不一定要走。要在原图中构造$st,en$,即有源汇。
不同的最小割:
最小割生成树,并不会证。
丢个板子好了。。。
1 #include<cstdio> 2 #include<iostream> 3 #include<queue> 4 #include<set> 5 using namespace std; 6 const int INF=(1<<30); 7 inline int min(int a,int b){return a<b?a:b; } 8 struct rr{ 9 int nt,to,c,yuan; 10 }bl[9100<<1];int hd[910],itot=1; 11 inline void addedge(int fr,int to,int c){ 12 bl[++itot]=(rr){hd[fr],to,c,c};hd[fr]=itot; 13 bl[++itot]=(rr){hd[to],fr,c,c};hd[to]=itot; 14 return ; 15 } 16 int N,M; 17 set<int >wg; 18 queue<int >dd; 19 int St,En; 20 int sd[910]; 21 bool bfs(){ 22 while(!dd.empty())dd.pop(); 23 for(int i=1;i<=N;++i)sd[i]=0; 24 dd.push(St);sd[St]=1; 25 while(!dd.empty()){ 26 int ltop=dd.front();dd.pop(); 27 for(int i=hd[ltop],y;i;i=bl[i].nt) 28 if(bl[i].c&&!sd[y=bl[i].to]){ 29 sd[y]=sd[ltop]+1; 30 if(y==En)return true; 31 dd.push(y); 32 } 33 } 34 return false; 35 } 36 int dfs(int u,int flow){ 37 if(u==En)return flow; 38 int rest=flow,k=0; 39 for(int i=hd[u],y;rest&&i;i=bl[i].nt) 40 if(bl[i].c&&sd[y=bl[i].to]==sd[u]+1){ 41 k=dfs(y,min(rest,bl[i].c)); 42 if(!k)sd[y]=0; 43 bl[i].c-=k,bl[i^1].c+=k,rest-=k; 44 } 45 return flow-rest; 46 } 47 int dinic(){ 48 int maxflow=0; 49 while(bfs())maxflow+=dfs(St,INF); 50 return maxflow; 51 } 52 int lin[910],xu[910]; 53 bool vis[910]; 54 inline void reback(){ 55 for(int i=2;i<=itot;++i)bl[i].c=bl[i].yuan; 56 for(int i=1;i<=N;++i)vis[i]=0; 57 return ; 58 } 59 void DFS(int u){ 60 vis[u]=1; 61 for(int i=hd[u],y;i;i=bl[i].nt) 62 if(bl[i].c&&!vis[y=bl[i].to])DFS(y); 63 return ; 64 } 65 void solve(int l,int r){ 66 if(l==r)return ; 67 reback(); 68 St=xu[l],En=xu[r]; 69 wg.insert(dinic()); 70 DFS(St); 71 int L=l,R=r; 72 for(int i=l;i<=r;++i){ 73 if(vis[xu[i]])lin[L++]=xu[i]; 74 else lin[R--]=xu[i]; 75 } 76 for(int i=l;i<=r;++i)xu[i]=lin[i]; 77 solve(l,L-1);solve(R+1,r); 78 return ; 79 } 80 int main(){ 81 //freopen("da.in","r",stdin); 82 83 scanf("%d%d",&N,&M); 84 for(int i=1,u,v,w;i<=M;++i){ 85 scanf("%d%d%d",&u,&v,&w); 86 addedge(u,v,w); 87 } 88 for(int i=1;i<=N;++i)xu[i]=i; 89 solve(1,N); 90 printf("%d\n",wg.size()); 91 92 return 0; 93 }
UPD:
关于上下界有源汇最大流,最小流。
先从$St$到$En$跑一遍可行流,有解情况应当满足$\sum totflow[i] (totflow[i]>0)$,然而这个流量并不是这个解的可行流,可行流应该是$st$到$en$流过的流量。
然后以最小流为例,删除$en->st$的边跑一遍$en$道$st$的最大流,即在满足可行流条件下的最大反向增广是能省去的最多流量。
所以答案即为$(St-En)可行流-(en-st)最大流$。
UPD:
学了一手$zkw$费用流,没想到就是$EK$费用流的多路增广版本。
写法基本和$dinic$一样,因为$spfa$不能分层,所以用个$vis$标记防止走环死循环。
适用范围:在费用流里多路增广出现的情况,大概是最短路有多条。
所以权值差异大时还是上$EK$了。。。
1 inline bool spfa(){ 2 queue<int >dd; 3 for(register int i=1;i<=En;++i)inq[i]=0,dist[i]=INF; 4 dd.push(St),dist[St]=0; 5 while(!dd.empty()){ 6 int ltop=dd.front();dd.pop();inq[ltop]=0; 7 for(register int i=hd[ltop],y;i;i=bl[i].nt) 8 if(bl[i].c&&dist[y=bl[i].to]>dist[ltop]+bl[i].w){ 9 dist[y]=dist[ltop]+bl[i].w; 10 if(!inq[y])inq[y]=1,dd.push(y); 11 } 12 } 13 return dist[En]<INF; 14 } 15 int maxflow=0,fy=0; 16 int dfs(int u,int flow){ 17 if(u==En)return flow; 18 int rest=flow,k=0;inq[u]=1; 19 for(int i=hd[u],y;rest&&i;i=bl[i].nt) 20 if(!inq[y=bl[i].to]&&bl[i].c&&dist[y]==dist[u]+bl[i].w){ 21 k=dfs(y,min(rest,bl[i].c)); 22 bl[i].c-=k,bl[i^1].c+=k,rest-=k,fy+=k*bl[i].w; 23 } 24 return flow-rest; 25 } 26 pair<int ,int >feiyong(){ 27 maxflow=0,fy=0; 28 while(spfa())maxflow+=dfs(St,INF); 29 return make_pair(maxflow,fy); 30 }