zoukankan      html  css  js  c++  java
  • 网络流/费用流题目总结[持续更新]

    //看心情填坑...

    飞行员配对方案问题 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
    ",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;
    }
    

      

  • 相关阅读:
    第19章_枚举:
    第14章_类型信息:
    第13章_字符串:
    纯css背景图自适应
    事务隔离机制_悲观锁_乐观锁
    hibernate 缓存
    list 和 iterate
    hibernate 性能优化之 1+N 问题
    QBC(Query By Criteria) QBE (Query By Example)
    HQL(Hibernate Query Language)
  • 原文地址:https://www.cnblogs.com/nervendnig/p/8948172.html
Copyright © 2011-2022 走看看