网络流24题

南楼画角 提交于 2019-12-04 08:47:16

1.飞行员配对方案问题

题目链接

二分图匹配模板题

2.负载平衡问题

题目链接

题意:

公司有 n个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等。如何用最少搬运量可以使 n个仓库的库存数量相同。搬运货物时,只能在相邻的仓库之间搬运。输出最少搬运量。

思路:

求出平均数,显然最后所有仓库的货物数量都是平均数。如果点u的货物数比平均数大w,则源点到u有一条容量为w;费用为0的边,如果点u的货物数比平均数小w,则u到汇点有一条容量为w,费用为0的边。相邻两点之间有容量无穷大,费用为1的边,跑费用流即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxm=1e3+5;
const int maxn=105;
const int INF=1e9;
struct edge {
    int v,next,w,f;
} E[maxm];
int tot=1,head[maxn];//tot必须设为1才能使反向边用^得到
void addedge(int u,int v,int w,int f) {
    E[++tot].v=v;
    E[tot].w=w;
    E[tot].f=f;
    E[tot].next=head[u];
    head[u]=tot;
}
int n,m,s,t;
int dis[maxn],vis[maxn];
int q[maxn];
bool spfa(){
    fill(dis,dis+n+1,INF);
    fill(vis,vis+n+1,0);
    int l=0,r=1;
    q[1]=t;
    dis[t]=0;//反向跑最短路
    while(l!=r){
        int u=q[l=l==n?0:l+1];
        vis[u]=0;
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v;
            int f=-E[i].f;//当前遍历的是反向的边,费用是负的
            if(E[i^1].w&&dis[v]>dis[u]+f){//反向的边才是流的时候走的边,容量要>0
                dis[v]=dis[u]+f;
                if(!vis[v]){
                    if(dis[v]>dis[l+1])q[r=r==n?0:r+1]=v;
                    else q[l]=v,l=l==0?n:l-1;//SLF优化
                    vis[v]=1;
                }
            }
        }
    }
    return dis[s]<INF;
}
int maxf,cost;
int dfs(int u,int flow){
    if(u==t){
        vis[t]=1;
        return flow;
    }
    int used=0;
    vis[u]=1;
    for(int i=head[u];i;i=E[i].next){
        int v=E[i].v,w=E[i].w,f=E[i].f;
        if(!vis[v]&&w&&dis[u]==dis[v]+f){//满足增广条件
            int temp=dfs(v,min(w,flow-used));
            if(temp){
                cost+=temp*f;
                E[i].w-=temp;
                E[i^1].w+=temp;
                used+=temp;
            }
            if(used==flow)break;
        }
    }
    return used;
}
void mincmaxf() {
    maxf=cost=0;
    while(spfa()) {
        vis[t]=1;
        while(vis[t]) {
            memset(vis,0,sizeof vis);
            maxf+=dfs(s,1e9);//一直増广直到走不到为止
        }
    }
}
void add(int u,int v,int w,int f){
    addedge(u,v,w,f);
    addedge(v,u,0,-f);
}
int a[105];
int main() {
    int nn;
    cin>>nn;
    n=nn+2;s=nn+1;t=nn+2;
    int ave=0;
    for(int i=1;i<=nn;i++){
        scanf("%d",&a[i]);
        ave+=a[i];
    }
    ave/=nn;
    for(int i=1;i<=nn;i++){
        if(a[i]>ave){
            add(s,i,a[i]-ave,0);
        }
        if(a[i]<ave){
            add(i,t,ave-a[i],0);
        }
    }
    for(int i=1;i<=nn-1;i++){
        add(i,i+1,1e9,1);
        add(i+1,i,1e9,1);
    }
    add(1,nn,1e9,1);
    add(nn,1,1e9,1);
    mincmaxf();
    printf("%d\n",cost);
}

3.运输问题

题目链接

题意:

公司有 m个仓库和 n个零售商店。第 i个仓库有 a个单位的货物;第 j个零售商店需要 b个单位的货物。

货物供需平衡,即\(\sum\limits_{i=1}^{m}a_i=\sum\limits_{j=1}^{n}b_j\)

从第i个仓库运送每单位货物到第 j 个零售商店的费用为Cij 。

问使所有货物都运走,总费用最大为多少,最小为多少?

思路:

求最小是最小费用最大流模板,求最大只要把边全部变成相反数的再跑一遍即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxm=1e5+5;
const int maxn=205;
const int INF=1e9;
struct edge {
    int v,next,w,f;
} E[maxm];
int tot=1,head[maxn];//tot必须设为1才能使反向边用^得到
void addedge(int u,int v,int w,int f) {
    E[++tot].v=v;
    E[tot].w=w;
    E[tot].f=f;
    E[tot].next=head[u];
    head[u]=tot;
}
int n,m,s,t;
int dis[maxn],vis[maxn];
int q[maxn];
bool spfa(){
    fill(dis,dis+n+1,INF);
    fill(vis,vis+n+1,0);
    int l=0,r=1;
    q[1]=t;
    dis[t]=0;//反向跑最短路
    while(l!=r){
        int u=q[l=l==n?0:l+1];
        vis[u]=0;
        for(int i=head[u];i;i=E[i].next){
            int v=E[i].v;
            int f=-E[i].f;//当前遍历的是反向的边,费用是负的
            if(E[i^1].w&&dis[v]>dis[u]+f){//反向的边才是流的时候走的边,容量要>0
                dis[v]=dis[u]+f;
                if(!vis[v]){
                    if(dis[v]>dis[l+1])q[r=r==n?0:r+1]=v;
                    else q[l]=v,l=l==0?n:l-1;//SLF优化
                    vis[v]=1;
                }
            }
        }
    }
    return dis[s]<INF;
}
int maxf,cost;
int dfs(int u,int flow){
    if(u==t){
        vis[t]=1;
        return flow;
    }
    int used=0;
    vis[u]=1;
    for(int i=head[u];i;i=E[i].next){
        int v=E[i].v,w=E[i].w,f=E[i].f;
        if(!vis[v]&&w&&dis[u]==dis[v]+f){//满足增广条件
            int temp=dfs(v,min(w,flow-used));
            if(temp){
                cost+=temp*f;
                E[i].w-=temp;
                E[i^1].w+=temp;
                used+=temp;
            }
            if(used==flow)break;
        }
    }
    return used;
}
void mincmaxf() {
    maxf=cost=0;
    while(spfa()) {
        vis[t]=1;
        while(vis[t]) {
            memset(vis,0,sizeof vis);
            maxf+=dfs(s,1e9);//一直増广直到走不到为止
        }
    }
}
void add(int u,int v,int w,int f){
    addedge(u,v,w,f);
    addedge(v,u,0,-f);
}
int a[105],b[105],c[105][105];
int main() {
    int nn,mm;
    cin>>mm>>nn;
    n=nn+mm+2;
    s=nn+mm+1;
    t=nn+mm+2;
    for(int i=1;i<=mm;i++){
        scanf("%d",&a[i]);
        add(s,i,a[i],0);
    }
    for(int i=1;i<=nn;i++){
        scanf("%d",&b[i]);
        add(mm+i,t,b[i],0);
    }
    for(int i=1;i<=mm;i++){
        for(int j=1;j<=nn;j++){
            scanf("%d",&c[i][j]);
            add(i,mm+j,1e9,c[i][j]);
        }
    }
    mincmaxf();
    printf("%d\n",cost);
    //重新建立费用为相反数的模型
    tot=1;
    memset(head,0,sizeof(head));
    for(int i=1;i<=mm;i++){
        add(s,i,a[i],0);
    }
    for(int i=1;i<=nn;i++){
        add(mm+i,t,b[i],0);
    }
    for(int i=1;i<=mm;i++){
        for(int j=1;j<=nn;j++){
            add(i,mm+j,1e9,-c[i][j]);
        }
    }
    mincmaxf();
    printf("%d\n",-cost);
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!