• 网络流24题 Updating


    p.s:

    ① 此随笔着重讲的是建模思路,可能不会贴上代码。

    ② 以下出现的S都代表源点,T都代表汇点。

    1、飞行员配对问题

    飞行员配对问题

    Solution:

    二分图匹配模板。可以用匈牙利算法,也可用最大流。

    网络流的建模:

    由S向左部图连容量为1的边,右部图向T连容量为1的边,左右部图间根据给出的条件,对应连上一条容量为1的边。

    2、分配问题

    分配问题

    二分图带权匹配模板。可以用KM算法,也可以用费用流。

    费用流的建模:

    在二分图最大匹配建图的基础上,给左右部图间的连边赋上1的费用,其他边费用为0。

    3、试题库问题

    试题库问题

    二分图多重匹配模板。

    网络流的建模:

    在二分图最大匹配建图的基础上,只需要把T连向左部图(左部图为题中试题类型)的边容量改为需要的试题量。

    4、圆桌问题

    实际上可以说应该说是多重匹配问题。

    这个合适上一个题的区别就在于,上一题只有左部图可以连多条边,而此题左右部图都可以。

    所以建模的话,只需在上述基础上,右部图再往T连对应容量的边即可。

    5、负载平衡问题

    负载平衡问题

    Solution:

    这个题其实费用流才是真暴力。。。数学做法只要O(nlogn)。

    但是,既然是24题当然得用网络流知识做嘛。。

    这个建模没那么好想:

    首先,我们需要注意到,一个点它只可以与前面的点或后面的点进行交换,且要么补给别人,要么接受别人补给。

    对于这种只有出入两种情况的,一般启发着往拆点二分图上靠。

    那么,左部图的点向右部图的点连的边表示左部给右部补给。

    然后,可以把所有的点需要或多余的给计算出来。

    对于一个点,如果它有多余,那么从它对应的左节点 向 相邻的两节点的右节点 连容量为inf,费用为1的边。

    然后,S连向这些节点的左节点,容量为多余的量,费用为0。

    对于一个点,如果它需要别人补给,那么向T连一条容量为需要的量的边。

    但是还有一个很重要的点!!

    那就是,对于有多余的点,还需向相邻节点的左节点连一条连容量为inf,费用为1的边!!!

    这就相当于:这个点并不直接向两侧的点供应,而是把他们当做中转站而已。。

    Code↓:

    #include<bits/stdc++.h>
    #define RG register
    #define IL inline 
    using namespace std;
    
    const int N=110;
    const int inf=0x3f3f3f3f;
    
    queue<int> q;
    int n,S,T,cost,s[N],a[N],tot=1,pre[N<<2],vis[N<<2],dis[N<<2],head[N<<2],Minf[N<<2];
    
    struct EDGE{int next,to,v,w;}e[N*N];
    
    IL void make(int a,int b,int c,int d) {
        e[++tot]=(EDGE){head[a],b,c,d},head[a]=tot;
        e[++tot]=(EDGE){head[b],a,0,-d},head[b]=tot;
    }
    
    IL int spfa() {
        RG int i,x,y;
        while(!q.empty()) q.pop();
        memset(vis,0,sizeof(vis));
        memset(dis,0x3f,sizeof(dis));
        q.push(S),vis[S]=1,dis[S]=0,Minf[S]=inf;
        while(!q.empty()) {
            x=q.front(),vis[x]=0,q.pop();
            for(i=head[x];i;i=e[i].next) {
                if(e[i].v&&dis[y=e[i].to]>dis[x]+e[i].w) {
                    dis[y]=dis[x]+e[i].w;
                    pre[y]=i,Minf[y]=min(Minf[x],e[i].v);
                    if(!vis[y]) vis[y]=1,q.push(y);
                }
            }
        }
        return dis[T]!=inf;
    }
    
    IL void update() {
        RG int i,now=T;
        while(now!=S) {
            i=pre[now];
            e[i].v-=Minf[T],e[i^1].v+=Minf[T];
            now=e[i^1].to;
        }
        cost+=Minf[T]*dis[T];
    }
    
    int main()
    {
        RG int i,ave,sum=0;
        scanf("%d",&n);
        for(i=1;i<=n;++i) scanf("%d",&s[i]),sum+=s[i];
        for(i=1,ave=sum/n;i<=n;++i) s[i]-=ave;
        S=1,T=2*n+2;
        for(i=1;i<=n;++i) {
            if(s[i]>0) make(S,i+1,s[i],0);
            else make(i+n+1,T,-s[i],0);
        }
        for(i=1;i<=n;++i) {
            if(i!=1) make(i+1,i,inf,1),make(i+1,i+n,inf,1);
            if(i!=n) make(i+1,i+2,inf,1),make(i+1,i+2+n,inf,1);
        }
        make(2,n+1,inf,1),make(2,n*2+1,inf,1);
        make(n+1,2,inf,1),make(n+1,n+2,inf,1);
        while(spfa()) update();
        printf("%d
    ",cost);
        return 0;
    }
    
    

    6、软件补丁问题

    软件补丁问题

    Solution:

    最短路!!(雾)

    一个需要注意的地方是,每个补丁可以用无限次。。。

    又由于错误数很少,所以,可以直接状压一下表示当前错误状态,跑最短路即可

    贴一下判断部分Code↓:

    IL int judge(int x,int id) {
    	if((x&b1[id])!=b1[id]||(x&b2[id])) return -1;
    	x-=x&f1[id],x|=f2[id];
    	return x;
    }
    

    7、孤岛营救问题

    孤岛营救问题

    Solution:

    BFS!!(额额额)

    思考一下如果需要知道什么?

    拥有的钥匙情况!

    不知道怎么办,设出来就好了。

    钥匙数小于等于10,直接状压后BFS即可。

    8、魔术球问题

    魔术球问题

    Solution:

    首先需要知道一个结论:

    放的球数和柱子数成正相关
    

    所以本题可以直接贪心(>_<)

    但是网络流怎么样做呢??

    还是考虑我们如果知道了什么东西就好做点了。

    球数和柱子数是吧。

    既然不知道,不妨尝试枚举,枚举基于上结论。

    考虑新进来一个球。

    那么由上结论可知,只要能放,就不会加入柱子。

    怎样判断一个球能不能放才是重点,网络流。

    我们考虑枚举之前能够和他连边的数,连上一条容量为1的边,

    如果,我们在当前残量网络上能跑出一条为1的增广路,那么就可以放,否则加柱子。

    Zeny神犇:这个题可以完全转换成最小路径覆盖啊。
    
    I:呃呃。。。
    
    Zeny神犇:你太呆了,这个题就相当于是给定了路径数求最大点数啊。
    
    菜鸡在神犇面前,只适合sto orz    
    

    9、汽车加油行驶问题

    汽车加油行驶问题

    Solution:

    直接最短路就好了。

    我写的spfa,而且是直接再网格上跑的,懒。

    需要注意一下转移的时候,边权可能需要讨论一下。

    10、最小路径覆盖问题

    最小路径覆盖问题

    Solution:

    路径最少意味着什么?

    终点数最少。这又意味着什么?

    没有出度的点最少。这在一张左右两部分别表示点入度和出度的二分图上又意味这什么?

    左部表示出度,没出度的点最少,就是左部没被匹配的点最少,那么就是:

    总点数-最大匹配。

    所以直接最大流。

    11、太空飞行计划问题

    太空飞行计划问题

    Solution:

    最大权闭合子图模板。

    闭合子图:

    给定一个有向图,从中选择一些点组成一个点集V.对于V中任意一个点,其后续节点都仍然在V中.
    

    最大权闭合子图的话,就是给一些边权值,有正有负,求权值最大的闭合子图。

    给个结论:

        最大权 闭合子图=正权之和-最小割/最大流
    

    建模:

    S向正权点连权值的边,负权点向T连权值绝对值的边,正权点负权点间建inf的边

    可以发现,这个还是很好理解的:

    S与正权点间的边如果满流了,那么意味着这些费用都用来填补负权了,没有收益。

    否则,这个差值就代表着选这个点及其后继点的收益。

    负权点与T同样可以类似分析。

    思维还是很巧妙的。

    12、方格取数问题

    方格取数问题

    Solution:

    首先,需要反向认识到:取数最大总和=所有数总和-不取的数最小的和

    这应该联想到最小割,那么考虑怎么建图:

    我们发现一个点能够影响的有且仅有它周围四个点,那么考虑黑白染色

    假设对于每一个黑点,我们向它四周的几个点连边,容量为无限.

    同时S向黑点连边,容量为黑点点权

    对于白点,白点向T连边,容量为白点点权

    这个时候跑最小割(最大流)即可

    此时我们割掉的那些边必然是非无限边,所以割一条边可以代表着:不选对应的那个格子。

    24/12ing

  • 相关阅读:
    查看mongodb的状态
    superset----缓存之redis
    superset--presto sql
    linux----之tcpdump小用
    Git版本回退的最佳方式
    SpringBoot 热部署
    不使用Tomcat,手写简单的web服务
    Spring Security 入门
    Maven总结
    git高级用法
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10617855.html
Copyright © 2020-2023  润新知