zoukankan      html  css  js  c++  java
  • 网络流初步

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

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

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


     士兵占领:

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

      $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) iin a$
          $addedge(i,En,cap) iin b$
          $addedge(i,j,INF) iin a ,jin 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
    ",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

      

  • 相关阅读:
    记录一次win10最大的bug
    Spring事务处理知识点
    ubuntu系统上安装docker
    Java抽象方法、接口、访问修饰符、访问权限笔记
    java牛客刷题2020年10月2日
    牛客错题2020年9月30日
    牛客错题2020年9月29日
    牛客刷题2020年9月27日
    java牛客刷题2020年9月21日
    java牛客刷题2020年9月20日
  • 原文地址:https://www.cnblogs.com/2018hzoicyf/p/11997417.html
Copyright © 2011-2022 走看看