$$AVICII$$

网络流初步

  已入土,没几道是自己想的。

  个人认为主体思想是反悔贪心,做题关键是建图。

  陈列一些颓过题解的大神题。


 士兵占领:

  转化题意,用总数减去最多的能丢掉的士兵数,变成最大流模型。

  $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 }
View Code

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 }
View Code

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

posted @ 2019-12-06 20:17  bootpuss  阅读(148)  评论(0编辑  收藏  举报