网络流24题 Updating

p.s:

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

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

1、飞行员配对问题

飞行员配对问题

Solution:

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

网络流的建模:

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

2、分配问题

分配问题

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

费用流的建模:

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

3、试题库问题

试题库问题

二分图多重匹配模板。

网络流的建模:

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

4、圆桌问题

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

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

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

5、负载平衡问题

负载平衡问题

这个题其实费用流才是真暴力。。。数学做法只要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;
}

猜你喜欢

转载自www.cnblogs.com/Bhllx/p/10617855.html