zoukankan      html  css  js  c++  java
  • 模板——最小费用最大流

    传送门:QAQQAQ

    因为要在最大流的情况下,保证最小费用,所以我们在增广时就用SPFA跑一个最短路进行增广,虽然这个路径可能不在最大流中,但残量网络可以保证我们这个“可以反悔的贪心”不会出错~

    具体写法:SPFA+EK

    代码:

    #include<bits/stdc++.h>
    #define mk make_pair
    using namespace std;
    typedef pair<int,int> pii;
    const int inf=(int)2e9; 
    const int N=1020*50;
    
    int n,m,s,t;
    int first[N*2],nxt[N*4],point[N*4],cost[N*4],flow[N*4],e=0;
    void add(int x,int y,int z,int c)
    {
        nxt[e]=first[x];
        first[x]=e;
        point[e]=y;
        cost[e]=c;
        flow[e]=z; e++;
    }
    void add_edge(int x,int y,int z,int c)
    {
        add(x,y,z,c);
        add(y,x,0,-c); 
    }
    
    int dis[N],inq[N],pre_point[N],f[N],pre_edge[N];
    void init_SPFA()
    {
        for(int i=1;i<=n;i++) dis[i]=inf,f[i]=inf;
        for(int i=1;i<=n;i++) inq[i]=0;
        memset(pre_point,-1,sizeof(pre_point));
        memset(pre_edge,-1,sizeof(pre_edge));
    }
    
    queue<int> q;
    bool SPFA()
    {
        init_SPFA();
        q.push(s); inq[s]=1; dis[s]=0;
        while(!q.empty())
        {
            int now=q.front(); q.pop();
            inq[now]=0;
            for(int i=first[now];i!=-1;i=nxt[i])
            {
                int pos=point[i];
                if(flow[i]==0) continue;
                if(dis[pos]>dis[now]+cost[i])
                {
                    dis[pos]=dis[now]+cost[i];
                    pre_point[pos]=now;
                    pre_edge[pos]=i;
                    f[pos]=min(flow[i],f[now]);
                    if(!inq[pos])
                    {
                        inq[pos]=1;
                        q.push(pos);
                    }
                }
            }
        }
        return pre_point[t]!=-1;
    }
    
    pii solve()
    {
        int max_flow=0,min_cost=0;
        int tot=0;
        while(SPFA())
        {
            min_cost+=dis[t]*f[t];
            max_flow+=f[t];
            int now=t;
            while(now!=s)
            {
                int E=pre_edge[now];
                flow[E]-=f[t];
                flow[E^1]+=f[t];
                now=pre_point[now];
            }
        }
        return mk(max_flow,min_cost);
    }
    
    void init()
    {
        memset(first,-1,sizeof(first));
        memset(nxt,-1,sizeof(nxt));
        scanf("%d%d%d%d",&n,&m,&s,&t);
        for(int i=1;i<=m;i++)//之前写成n了啊啊啊 
        {
            int x,y,z,c;
            scanf("%d%d%d%d",&x,&y,&z,&c);
            add_edge(x,y,z,c);
        }
    }
    
    int main()
    {
        init();
        pii ans=solve();
        cout<<ans.first<<" "<<ans.second<<endl;
    }
    View Code

    再来一道题:

    传送门:QAQQAQ

    题意:有$n$个盒子(1<=$n$<=1000)围成一个圈,每个盒子有$a_{i}$个球,所有盒子的球的总数小于等于$n$.每一次移动,可以把一个球移动到它的一个相邻的盒子内.
    现在要使得每个盒子的球数$<=1$,求最少的移动次数

    思路:直接说建图吧(网络流主要就是难在建图)

    我们造一个源点,一个汇点,源点和每一个点连一条容量为$a_{i}$,费用为$0$的边,汇点和每一个点连一条容量为$1$,费用为$0$的边,对于中间的点,我们对于它和它左右两点各连一条容量为$inf$,费用为$1$的边。

    这样我们跑一遍最小费用最大流即可(即在总流量为$sumball$的情况下,费用最小)

    代码:

    #include<bits/stdc++.h>
    #define mk make_pair
    using namespace std;
    typedef pair<int,int> pii;
    const int inf=(int)2e9;
    const int N=100020;
     
    int n,m,s,t;
    int first[N*2],nxt[N*4],point[N*4],cost[N*4],flow[N*4],e=0;
    void add(int x,int y,int z,int c)
    {
        nxt[e]=first[x];
        first[x]=e;
        point[e]=y;
        cost[e]=c;
        flow[e]=z; e++;
    }
    void add_edge(int x,int y,int z,int c)
    {
        add(x,y,z,c);
        add(y,x,0,-c);
    }
     
    int dis[N],inq[N],pre_point[N],f[N],pre_edge[N];
    void init_SPFA()
    {
        for(int i=0;i<=n+1;i++) dis[i]=inf,f[i]=inf;
        for(int i=0;i<=n+1;i++) inq[i]=0;
        memset(pre_point,-1,sizeof(pre_point));
        memset(pre_edge,-1,sizeof(pre_edge));
    }
     
    queue<int> q;
    bool SPFA()
    {
        init_SPFA();
        q.push(s); inq[s]=1; dis[s]=0;
        while(!q.empty())
        {
            int now=q.front(); q.pop();
            inq[now]=0;
            for(int i=first[now];i!=-1;i=nxt[i])
            {
                int pos=point[i];
                if(flow[i]==0) continue;
                if(dis[pos]>dis[now]+cost[i])
                {
                    dis[pos]=dis[now]+cost[i];
                    pre_point[pos]=now;
                    pre_edge[pos]=i;
                    f[pos]=min(flow[i],f[now]);
                    if(!inq[pos])
                    {
                        inq[pos]=1;
                        q.push(pos);
                    }
                }
            }
        }
        return pre_point[t]!=-1;
    }
     
    int solve()
    {
        int max_flow=0,min_cost=0;
        int tot=0;
        while(SPFA())
        {
            min_cost+=dis[t]*f[t];//是单位费用,所以要乘
            max_flow+=f[t];
            int now=t;
            while(now!=s)
            {
                int E=pre_edge[now];
                flow[E]-=f[t];
                flow[E^1]+=f[t];
                now=pre_point[now];
            }
        }
        return min_cost;
    }
     
    int a[N];
    void init()
    {
        memset(first,-1,sizeof(first));
        memset(nxt,-1,sizeof(nxt));
        scanf("%d",&n);
        s=0,t=n+1;
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)
        {
            add_edge(s,i,a[i],0);
            add_edge(i,t,1,0);
            int l=i-1,r=i+1;
            if(i==1) l=n; if(i==n) r=1;
            add_edge(i,l,inf,1);
            add_edge(i,r,inf,1);
        }
    }
     
    int main()
    {
        init();
        int ans=solve();
        cout<<ans<<endl;
    }
    View Code
  • 相关阅读:
    【linux】Centos下登陆mysql报错#1045
    tomcat在centos7里面启动很慢的解决办法
    tomcat日志文件 转载https://www.cnblogs.com/operationhome/p/9680040.html
    tomcat的文件目录结构
    centos 7服务器下tomcat 问题 1.配置问题
    x11转发遇到的问题
    x11转发,可以在shell里面看到图形界面
    linux里面tomcat配置遇到的问题
    vim中文乱码 vim字符集设置
    c#.net常见字符串处理方法
  • 原文地址:https://www.cnblogs.com/Forever-666/p/11279706.html
Copyright © 2011-2022 走看看