网络流/费用流题目总结[持续更新]

//看心情填坑...

飞行员配对方案问题 24题

裸二分图匹配,跑一边Dinic即可,原理类似网络流的Hopcroft-Karp算法在二分图的时间复杂度可以到达$O( sqrt(n)* m )$ 

   实际上Dinic的原理和它类似,实现中也可以跑的很快

#include <bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define pp pair<int,int>
#define rep(ii,a,b) for(auto ii=a;ii<=b;ii++)
#define	per(ii,a,b) for(auto ii=a;ii>=b;ii--)
#define show(x) cout<<#x<<"="<<x<<endl
#define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
#define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
using namespace std;
const int maxn=1e5+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
int to[maxm],cap[maxm],nex[maxm];
int head[maxn],S,T,nume,ans,dis[maxn];
int q[maxn];
void add(int a,int b,int c){
    to[nume]=b,cap[nume]=c,nex[nume]=head[a];
    head[a]=nume++;
    to[nume]=a,cap[nume]=0,nex[nume]=head[b];
    head[b]=nume++;
}
bool bfs(){
    memset(dis,-1,sizeof dis);
    int top=0,end=0;
    q[end++]=S;
    dis[S]=0;
    while(top!=end){
        int now=q[top++];top%=maxn;
        for(int i=head[now];~i;i=nex[i])
            if(dis[to[i]]==-1&&cap[i]){
                dis[to[i]]=dis[now]+1;
                q[end++]=to[i];end%=maxn;
            }
    }
    return dis[T]!=-1;
}
int dfs(int now,int last){
    if(now==T) return last;
    int used=0,flow;
    for(int i=head[now];~i;i=nex[i]){
        if(cap[i]&&dis[to[i]]==dis[now]+1){
            flow=last-used;
            flow=dfs(to[i],min(flow,cap[i]));
            used+=flow;
            cap[i]-=flow;
            cap[i^1]+=flow;
            if(used==last) return last;
        }
    }
    if(used==0) dis[now]=-1;
    return used;
}
void dinic(){
    ans=0;
    while(bfs()) ans+=dfs(S,0x3f3f3f3f);
}
int main(){
//#define test
#ifdef test
    freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
    cin>>m>>n;
    S=0;T=n+1;
    memset(head,-1,sizeof head);
    int a,b;
    while(cin>>a>>b,~a&&~b) add(a,b,1);
    for(int i=1;i<=m;i++) add(S,i,1);
    for(int i=m+1;i<=n;i++) add(i,T,1);
    dinic();
    if(ans) {
        cout<<ans<<endl;
        for(int i=0;i<nume;i+=2) if(to[i]!=S&&to[i^1]!=S&&to[i]!=T&&to[i^1]!=T&&cap[i^1]!=0){
                printf("%d %d\n",to[i^1],to[i]);
            }
    }
    else cout<<"No Solution!"<<endl;
#ifdef test
    fclose(stdin);fclose(stdout);system("out.txt");
#endif
    return 0;
}

负载平衡问题 24题

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

求最小搬运量

最小费用最大流,首先按照题意所说,建立一个环形的路线,费用为1,容量$INF$

其次,对于大于平均值的,需要向其他仓库输出,换句话说都是些小源点,那就从源点向他链接一条边,容量为多余量,费用为0,

然后,相对应的,对于小于平均值的,需要接受,换句话说都是小汇点,链接到汇点的边,容量为需要的量,费用也是0,

最后跑一边最小费用最大流,由于建图方式,超级源点流出的肯定等于超级汇点收到的,

且流出的仓库肯定不会多输出,流入的仓库也不会多接受,符合要求.

#include <bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define pp pair<int,int>
#define rep(ii,a,b) for(auto ii=a;ii<=b;ii++)
#define per(ii,a,b) for(auto ii=a;ii>=b;ii--)
#define show(x) cout<<#x<<"="<<x<<endl
#define show2(x,y) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl
#define show3(x,y,z) cout<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl
using namespace std;
const int maxn=10000+10;
const int maxm=10000+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
struct node {
    int pre,to,cap,cost,next;
}e[maxm];
int head[maxn],nume,inq[maxn],sum,ans;
int que[maxn],pre[maxn],dis[maxn];
int num[maxn],S,T;
inline void addedge(int a,int b,int c,int d){
    e[++nume]={a,b,c,d,head[a]};
    head[a]=nume;
}
inline void add(int a,int b,int c,int d){
    addedge(a,b,c,d);addedge(b,a,0,-d);
}
bool spfa(){
    for(int i=0;i<=T;i++)dis[i]=INF;
    dis[S]=que[0]=S;
    int top=0,end=1;
    while(top!=end){
        int now=que[top++];top%=maxn;
        for(int i=head[now];i;i=e[i].next){
            if(e[i].cap&&dis[e[i].to]>dis[now]+e[i].cost){
                pre[e[i].to]=i;
                dis[e[i].to]=dis[now]+e[i].cost;
                if(!inq[e[i].to]){
                    inq[e[i].to]=true;
                    que[end++]=e[i].to;end%=maxn;
                }
            }
        }
        inq[now]=false;
    }
    return dis[T]!=INF;
}
void dfs(){
    int flow=INF;
    for(int i=pre[T];i;i=pre[e[i].pre]) flow=min(flow,e[i].cap);
    for(int i=pre[T];i;i=pre[e[i].pre]) {
        e[i].cap-=flow;
        e[i^1].cap+=flow;
        ans+=e[i].cost*flow;
    }
}
void mcf(){
    ans=0;
    while(spfa()) dfs();
}
int main(){
//#define test
#ifdef test
    freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
    IO;
    cin>>n;
    nume=1;
    S=0,T=3*n;
    for(int i=1;i<=n;i++) cin>>num[i],sum+=num[i];
    sum/=n;
    for(int i=1;i<=n;i++) num[i]-=sum;
    for(int i=1;i<=n;i++){
        if(num[i]>0) add(S,i,num[i],0);
        else add(i+n,T,-num[i],0);
    }
    for(int i=1;i<=n;i++){
        if(i!=1){
            add(i,i-1,INF,1);
            add(i,i-1+n,INF,1);
        }
        if(i!=n){
            add(i,i+1,INF,1);
            add(i,i+1+n,INF,1);
        }
    }
    add(n,1,INF,1);
    add(n,1+n,INF,1);
    add(1,n,INF,1);
    add(1,n<<1,INF,1);
    mcf();
    cout<<ans<<endl;
#ifdef test
    fclose(stdin);fclose(stdout);system("out.txt");
#endif
    return 0;
}

传纸条

两个人在一个棋盘上互相交换纸条,从左上到右下,第一个人的纸条只能向右或向下,反之亦然,

每一个点只能经过一次,并且有一个权值$G(i,j)$,要求两个纸条最终经过的所有点权值和最大

最大费用最大流....但是这个题标解应该是DP吧

两个人互相传,实际上等于一个人同时传了2个到下面,于是简化问题,

一开始朴素的建了图,结果错了2个CASE,

方式就是超级源向$(1,1)$连接一条费用0,容量2的边,超级汇点一个道理,然后就用容量1.费用为$G(i,j)$来模拟..最后发现会出现可能会出现一个点通过大小为2的流这种情况,题目实际上是限制每个点的最大流量...

所以..拆点大法好,每个点拆为2个点,一个入,一个出,从入到出连接2个边,一个费用为$G(i,j)$容量为1,一个费用为0,容量为1,这样即使有一个点通过大小为2的流量,也不会导致流量重复计算了

#include <bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define rep(ii,a,b) for(auto ii=a;ii<=b;ii++)
using namespace std;
const int maxn=1e5+10;
const int maxm=1e6+10;
const int INF=0x3f3f3f3f;
int casn,n,m,k;
int g[123][123];
struct node {
    int pre,to,cap,cost,next;
}e[maxm];
int head[maxn],nume,inq[maxn],sum;
int que[maxn],pre[maxn],dis[maxn];
int S,T,ans;
inline void addx(int a,int b,int c,int d){
    e[++nume]={a,b,c,d,head[a]};
    head[a]=nume;
}
inline void add(int a,int b,int c,int d){
    addx(a,b,c,d);addx(b,a,0,-d);
}
bool spfa(){
    rep(i,0,T) dis[i]=-INF;
    int top=0,end=1;
    dis[S]=que[0]=0;
    while(top!=end){
        int now=que[top++];top%=maxn;
        for(int i=head[now];i;i=e[i].next){
            if(e[i].cap&&dis[e[i].to]<dis[now]+e[i].cost){
                pre[e[i].to]=i;
                dis[e[i].to]=dis[now]+e[i].cost;
                if(!inq[e[i].to]){
                    inq[e[i].to]=true;
                    que[end++]=e[i].to;end%=maxn;
                }
            }
        }
        inq[now]=false;
    }
    return dis[T]!=-INF;
}
void dfs(){
    int d=INF;
    for(int i=pre[T];i;i=pre[e[i].pre]) d=min(d,e[i].cap);
    for(int i=pre[T];i;i=pre[e[i].pre]) {
        e[i].cap-=d;
        e[i^1].cap+=d;
        ans+=e[i].cost*d;
    }
}
int main(){
//#define test
#ifdef test
    freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
#endif
		IO;
    cin>>n>>m;
    rep(i,1,n){
        rep(j,1,m){
            cin>>g[i][j];
        }
    }
    S=0,T=3*n*m;
    int gap=n*m+2;
    nume=1;
    add(S,1,2,0);
    add(m*n,T,2,0);
    rep(i,1,n){
        rep(j,1,m){
            int s=(i-1)*m+j;
            if(j<m||i<n){
								if(i==1&&j==1) add(s,s+gap,2,0);
                else add(s,s+gap,1,g[i][j]);
                if(j<m) add(s+gap,s+1,1,0);
                if(i<n) add(s+gap,s+m,1,0);
            }
        }
    }
    ans=0;
    while(spfa()) {
        dfs();
    }
    cout<<abs(ans)<<endl;
#ifdef test
    fclose(stdin);fclose(stdout);system("out.txt");
#endif
    return 0;
}

  

猜你喜欢

转载自www.cnblogs.com/nervendnig/p/8948172.html