费用流(直接应用)

1.概念

所有最大可行流中费用的最小/最大值
image
上图中的最小费用最大流就是15
每条边都有一个权值w,这条边如果流量是c,那么这条边的费用就是c*w

2.求法

把EK算法中的bfs函数换成spfa求源点到汇点的一条最短路即可
模板(普通)

#include<bits/stdc++.h>

#define x first
#define y second
#define endl '\n'
#define int long long
 
using namespace std;

const int N=200010,M=1000010,INF=1e15;//根据边的大小,来调整N,M,INF

typedef pair<int,int> PII;

int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];

void add(int a, int b, int c, int d)
{
    e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}

bool spfa()
{
    int hh = 0, tt = 1;
    memset(d, 0x3f, sizeof d);
    memset(incf, 0, sizeof incf);
    q[0] = S, d[S] = 0, incf[S] = INF;
    while (hh != tt)
    {
        int t = q[hh ++ ];
        if (hh == N) hh = 0;
        st[t] = false;

        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (f[i] && d[ver] > d[t] + w[i])
            {
                d[ver] = d[t] + w[i];
                pre[ver] = i;
                incf[ver] = min(f[i], incf[t]);
                if (!st[ver]){
                    q[tt++]=ver;
                    if (tt==N) tt=0;
                    st[ver]=true;
                }
            }
        }
    }

    return incf[T]>0;
}

void EK(int& flow, int& cost){
    flow=cost=0;
    while(spfa()){
        int t=incf[T];
        flow+=t,cost+=t*d[T];
        for (int i=T;i!=S;i=e[pre[i] ^ 1]){
            f[pre[i]]-=t;
            f[pre[i]^1]+=t;
        }
    }
}

void slove(){
	cin>>n>>m>>S>>T;
	memset(h,-1,sizeof h);

	while(m--){
		int a,b,c,d;
		cin>>a>>b>>c>>d;
		add(a,b,c,d);
	}
	int flow,cost;
	EK(flow,cost);
	cout<<flow<<" "<<cost<<endl;
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	int T=1;
	while(T--) slove();
}

dijkstra势能优化+spfa(快一倍)

// Author: YE Minghan
#include<bits/stdc++.h>
using namespace std;
#ifdef DEBUG
#include "templates/debug.h"
#else
#define dbg(...) (void)0
#define here (void)0
#endif
using ll=long long;
using VI=vector<int>;
using PII=pair<int,int>;
#define endl '\n'
#define PB emplace_back
#define PPB pop_back
#define MP make_pair
#define ALL(Name) Name.begin(),Name.end()
#define GI greater<int>
#define fi first
#define se second

struct MCMF
{
	private:
	static const ll INF=4e18;
	int n,m,S,T;
	struct E
	{
		int v;
		ll cap,cost;
		int rev;
	};
	vector<vector<E>> g;
	vector<ll> dis,h;
	vector<PII> pre;
	void spfa()
	{
		queue<int>q;
		fill(h.begin()+1,h.end(),0);
		h[S]=0,q.push(S);
		while(!q.empty())
		{
			int x=q.front();q.pop();
			for(int i=0;i<g[x].size();i++)
			{
				auto [y,f,c,r]=g[x][i];
				if(f&&h[y]>h[x]+c)h[y]=h[x]+c,q.push(y);
			}
		}
	}
	void dijk()
	{
		priority_queue<pair<ll,int>,vector<pair<ll,int>>,greater<> >q;
		fill(dis.begin()+1,dis.end(),INF);
		dis[S]=0,q.emplace(0,S);
		while(!q.empty())
		{
			auto [d,x]=q.top();q.pop();
			if(dis[x]!=d)continue;
			for(int i=0;i<g[x].size();i++)
			{
				auto [y,f,c,r]=g[x][i];
				if(f&&dis[y]>dis[x]+c+h[x]-h[y])pre[y]={x,i},q.emplace(dis[y]=dis[x]+c+h[x]-h[y],y);
			}
		}
	}

	public:
	MCMF(){}
	MCMF(int node,int sink,int terminal)
	{
		n=node,S=sink,T=terminal;
		g=vector<vector<E>>(node+1);
		dis=h=vector<ll>(node+1);
		pre=vector<PII>(node+1);
	}
	void add(int u,int v,ll cap,ll cost)
	{
		g[u].PB(E{v,cap, cost,(int)g[v].size()  });
		g[v].PB(E{u,0  ,-cost,(int)g[u].size()-1});
	}
	pair<ll,ll> Flow(ll flow=INF)
	{
		ll totflow=0,totcost=0;
		spfa();
		while(totflow<flow)
		{
			dijk();
			if(dis[T]>=INF/2)break;
			ll co=0,fl=flow-totflow;
			for(int i=T;i!=S;)
			{
				auto [u,id]=pre[i];
				auto [v,f,c,r]=g[u][id];
				co+=c,fl=min(fl,f);
				i=u;
			}
			totflow+=fl,totcost+=fl*co;
			for(int i=T;i!=S;)
			{
				auto [u,id]=pre[i];
				auto &[v,f,c,r]=g[u][id];
				f-=fl,g[v][r].cap+=fl;
				i=u;
			}
			for(int i=1;i<=n;i++)h[i]+=dis[i];
		}
		return {totflow,totcost};
	}
};

int main()
{
	ios::sync_with_stdio(false),cin.tie(nullptr);
//	int _;cin>>_;while(_--)

	int n,m,S,T;
	cin>>n>>m>>S>>T;
	MCMF f(n,S,T);
	for(int i=1,u,v,w,c;i<=m;i++)
		cin>>u>>v>>w>>c,f.add(u,v,w,c);
	auto [fl,co]=f.Flow();
	cout<<fl<<" "<<co<<endl;

	return 0;
}

运输问题

image

#include<bits/stdc++.h>

#define x first
#define y second
#define endl '\n'
#define int long long
 
using namespace std;

const int N=200010,M=1000010,INF=1e15;//根据边的大小,来调整N,M,INF

typedef pair<int,int> PII;

int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];

void add(int a, int b, int c, int d)
{
    e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}

bool spfa()
{
    int hh = 0, tt = 1;
    memset(d, 0x3f, sizeof d);
    memset(incf, 0, sizeof incf);
    q[0] = S, d[S] = 0, incf[S] = INF;
    while (hh != tt)
    {
        int t = q[hh ++ ];
        if (hh == N) hh = 0;
        st[t] = false;

        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (f[i] && d[ver] > d[t] + w[i])
            {
                d[ver] = d[t] + w[i];
                pre[ver] = i;
                incf[ver] = min(f[i], incf[t]);
                if (!st[ver]){
                    q[tt++]=ver;
                    if (tt==N) tt=0;
                    st[ver]=true;
                }
            }
        }
    }

    return incf[T]>0;
}

void EK(int& flow, int& cost){
    flow=cost=0;
    while(spfa()){
        int t=incf[T];
        flow+=t,cost+=t*d[T];
        for (int i=T;i!=S;i=e[pre[i] ^ 1]){
            f[pre[i]]-=t;
            f[pre[i]^1]+=t;
        }
    }
}

void slove(){
    cin>>m>>n;

    S=0,T=m+n+1;

    memset(h,-1,sizeof h);

    for(int i=1;i<=m;i++){
        int x;cin>>x;
        add(S,i,x,0);
    }

    for(int i=1;i<=n;i++){
        int x;cin>>x;
        add(m+i,T,x,0);
    }

    for(int i=1;i<=m;i++)
     for(int j=1;j<=n;j++){
        int x;cin>>x;
        add(i,m+j,INF,x);
     }
    
    int flow,cost;
    EK(flow,cost);
    cout<<cost<<endl;

    for(int i=0;i<idx;i+=2){
        f[i]+=f[i^1],f[i^1]=0;
        w[i]=-w[i],w[i^1]=-w[i^1];
    }
    EK(flow,cost);
    cout<<-cost<<endl;
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	int T=1;
	while(T--) slove();
}

image

#include<bits/stdc++.h>

#define x first
#define y second
#define endl '\n'
#define int long long
 
using namespace std;

const int N=200010,M=1000010,INF=1e15;//根据边的大小,来调整N,M,INF

typedef pair<int,int> PII;

int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];
int s[N];

void add(int a, int b, int c, int d)
{
    e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}

bool spfa()
{
    int hh = 0, tt = 1;
    memset(d, 0x3f, sizeof d);
    memset(incf, 0, sizeof incf);
    q[0] = S, d[S] = 0, incf[S] = INF;
    while (hh != tt)
    {
        int t = q[hh ++ ];
        if (hh == N) hh = 0;
        st[t] = false;

        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (f[i] && d[ver] > d[t] + w[i])
            {
                d[ver] = d[t] + w[i];
                pre[ver] = i;
                incf[ver] = min(f[i], incf[t]);
                if (!st[ver]){
                    q[tt++]=ver;
                    if (tt==N) tt=0;
                    st[ver]=true;
                }
            }
        }
    }

    return incf[T]>0;
}

int EK(){
    int cost=0;
    while(spfa()){
        int t=incf[T];
        cost+=t*d[T];
        for (int i=T;i!=S;i=e[pre[i] ^ 1]){
            f[pre[i]]-=t;
            f[pre[i]^1]+=t;
        }
    }
    return cost;
}

void slove(){
    cin>>n;
    S=0,T=n+1;
    memset(h,-1,sizeof h);

    int tot=0;
    for(int i=1;i<=n;i++){
        cin>>s[i];
        //先对相邻的两点之间连边
        tot+=s[i];
        add(i,i<n?i+1:1,INF,1);
        add(i,i>1?i-1:n,INF,1);
    }

    tot/=n;

    for(int i=1;i<=n;i++){
        if(tot<s[i]) add(S,i,s[i]-tot,0);//高于平均值的,需要流出s[i]-tot的货物
        else if(tot>s[i]) add(i,T,tot-s[i],0);//低于的需要被入tot-s[i]的货物
    }

    cout<<EK()<<endl;
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	int T=1;
	while(T--) slove();
}

image

#include<bits/stdc++.h>

#define x first
#define y second
#define endl '\n'
#define int long long
 
using namespace std;

const int N=200010,M=1000010,INF=1e15;//根据边的大小,来调整N,M,INF

typedef pair<int,int> PII;

int n, m, S, T;
int h[N], e[M], f[M], w[M], ne[M], idx;
int q[N], d[N], pre[N], incf[N];
bool st[N];

void add(int a, int b, int c, int d)
{
    e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx ++ ;
    e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx ++ ;
}

bool spfa()
{
    int hh = 0, tt = 1;
    memset(d, 0x3f, sizeof d);
    memset(incf, 0, sizeof incf);
    q[0] = S, d[S] = 0, incf[S] = INF;
    while (hh != tt)
    {
        int t = q[hh ++ ];
        if (hh == N) hh = 0;
        st[t] = false;

        for (int i = h[t]; ~i; i = ne[i])
        {
            int ver = e[i];
            if (f[i] && d[ver] > d[t] + w[i])
            {
                d[ver] = d[t] + w[i];
                pre[ver] = i;
                incf[ver] = min(f[i], incf[t]);
                if (!st[ver]){
                    q[tt++]=ver;
                    if (tt==N) tt=0;
                    st[ver]=true;
                }
            }
        }
    }

    return incf[T]>0;
}

int EK(){
    int cost=0;
    while(spfa()){
        int t=incf[T];
        cost+=t*d[T];
        for (int i=T;i!=S;i=e[pre[i] ^ 1]){
            f[pre[i]]-=t;
            f[pre[i]^1]+=t;
        }
    }
    return cost;
}

void slove(){
    cin>>n;
    memset(h,-1,sizeof h);
    S=0,T=2*n+1;
    for(int i=1;i<=n;i++){
        add(S,i,1,0);
        add(n+i,T,1,0);
    }

    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
        int x;
        cin>>x;
        add(i,n+j,1,x);
    }

    cout<<EK()<<endl;

    for(int i=0;i<idx;i+=2){
        f[i]+=f[i^1],f[i^1]=0;
        w[i]=-w[i],w[i^1]=-w[i^1];
    }

    cout<<-EK()<<endl;
}

signed main(){
	ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
	int T=1;
	while(T--) slove();
}

image
规则一:直接拆点即可,出点与入点之间的容量为1,点与点之间的边的容量也为1
规则二:点与点之间的边的容量设为1即可
规则三:不限制容量

int main()
{
    int cnt = 0;
    scanf("%d%d", &m, &n);
    S = ++ cnt;
    T = ++ cnt;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m + i - 1; j ++ )
        {
            scanf("%d", &cost[i][j]);
            id[i][j] = ++ cnt;
        }

    // 规则1
    memset(h, -1, sizeof h), idx = 0;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m + i - 1; j ++ )
        {
            //id[i][j]*2表示入点,id[i][j]*2+1表示出点
            add(id[i][j] * 2, id[i][j] * 2 + 1, 1, cost[i][j]);//该点的入点向出点连边
            if (i == 1) add(S, id[i][j] * 2, 1, 0);//源点向起点的入点连边
            if (i == n) add(id[i][j] * 2 + 1, T, 1, 0);//终点的出点向汇点连边
            if (i < n)
            {
                add(id[i][j] * 2 + 1, id[i + 1][j] * 2, 1, 0);
                add(id[i][j] * 2 + 1, id[i + 1][j + 1] * 2, 1, 0);
            }
        }
    printf("%d\n", EK());

    // 规则2
    memset(h, -1, sizeof h), idx = 0;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m + i - 1; j ++ )
        {
            add(id[i][j] * 2, id[i][j] * 2 + 1, INF, cost[i][j]);
            if (i == 1) add(S, id[i][j] * 2, 1, 0);
            if (i == n) add(id[i][j] * 2 + 1, T, INF, 0);
            if (i < n)
            {
                add(id[i][j] * 2 + 1, id[i + 1][j] * 2, 1, 0);
                add(id[i][j] * 2 + 1, id[i + 1][j + 1] * 2, 1, 0);
            }
        }
    printf("%d\n", EK());

    // 规则3
    memset(h, -1, sizeof h), idx = 0;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m + i - 1; j ++ )
        {
            add(id[i][j] * 2, id[i][j] * 2 + 1, INF, cost[i][j]);
            if (i == 1) add(S, id[i][j] * 2, 1, 0);
            if (i == n) add(id[i][j] * 2 + 1, T, INF, 0);
            if (i < n)
            {
                add(id[i][j] * 2 + 1, id[i + 1][j] * 2, INF, 0);
                add(id[i][j] * 2 + 1, id[i + 1][j + 1] * 2, INF, 0);
            }
        }
    printf("%d\n", EK());

    return 0;
}
posted @ 2025-10-22 16:59  MENDAXZ  阅读(7)  评论(0)    收藏  举报