网络流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\n",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

posted @ 2019-03-28 21:32  薄荷凉了夏  阅读(142)  评论(0编辑  收藏  举报