最小费用最大流

练习题

P3381 【模板】最小费用最大流

在这里插入图片描述
链接:https://www.luogu.com.cn/problem/P3381

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;

namespace MCMF
{
	const int MAXN=5000+10,MAXM=1e5+10,inf=2e9;
	int dis[MAXN],visit[MAXN],pre[MAXN],maxflow,mincost;
	int maxvex,head[maxn],ecnt,s,t;
	
	void init(int ss,int tt,int n)
	{
	    ecnt=1,maxvex=n;
	    for(int i=0; i<=maxvex; ++i) head[i]=-1;
	    s=ss,t=tt;
	}
	struct Edge
	{
	    int nxt,to,flow,cost;
	} edges[MAXM];
	void add(int u,int v,int flow,int cost)
	{
	    edges[++ecnt]= {head[u],v,flow,cost},head[u]=ecnt;
	    edges[++ecnt]= {head[v],u,0,-cost},head[v]=ecnt;
	}
	bool spfa()
	{
	    for(int i=0; i<=maxvex; ++i) dis[i]=inf,visit[i]=pre[i]=0;
	    dis[s]=0;
	    queue<int> q;
	    q.push(s);
	    visit[s]=1;
	    while(!q.empty())
	    {
	        int u=q.front();
	        q.pop();
	        visit[u]=0;
	        for(int i=head[u]; i!=-1; i=edges[i].nxt)
	        {
	            int v=edges[i].to,cost=edges[i].cost;
	            if(edges[i].flow&&dis[v]>dis[u]+cost)
	            {
	                dis[v]=dis[u]+cost;
	                pre[v]=i;
	                if(!visit[v]) visit[v]=1,q.push(v);
	            }
	        }
	    }
	    return dis[t]<inf;
	}
	void augment()
	{
	    int p=t,curflow=inf;
	    while(pre[p])
	    {
	        curflow=min(curflow,edges[pre[p]].flow);
	        p=edges[pre[p]^1].to;
	    }
	    maxflow+=curflow,mincost+=curflow*dis[t];
	    p=t;
	    while(pre[p])
	    {
	        edges[pre[p]].flow-=curflow;
	        edges[pre[p]^1].flow+=curflow;
	        p=edges[pre[p]^1].to;
	    }
	}
	void mcmf()
	{
	    maxflow=0,mincost=0;
	    while(spfa()) augment();
	}
};
int n,m,s,t;
int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    MCMF::init(s,t,n);
    for(int i=1; i<=m; ++i)
    {
        int u,v,w,c;
        scanf("%d%d%d%d",&u,&v,&w,&c);
        MCMF::add(u,v,w,c);
    }
    MCMF::mcmf();
    printf("%d %d\n",MCMF::maxflow,MCMF::mincost);
    return 0;
}

H. Minimum-cost Flow 2020牛客第一场 (最小费用最大流)

链接:https://ac.nowcoder.com/acm/contest/5666/H

题意:给定一张带费用的图,单位费用为 c i c_i ci,给出 q 次询问,每次限定每条边的容量为 u i v i \frac {u_i}{v_i} viui ,问从 1 到 n 流过 1 的流量需要的最小费用是多少

思路:设 c o s t ( x , y ) cost(x,y) cost(x,y) 表示每条边的容量为 x x x ,流过 y y y 流量的最小花费,则
c o s t ( u i v i , 1 ) = c o s t ( 1 , v i u i ) × u i v i cost(\frac {u_i}{v_i},1) =cost(1,\frac{v_i}{u_i})\times \frac{u_i}{v_i} cost(viui,1)=cost(1,uivi)×viui

因此,只需要将边的容量设为 1 ,做一次费用流,记录每条增广路得到的最小费用。然后计算流过 v i u i \frac{v_i}{u_i} uivi 的流量需要的费用,最后乘上 u i v i \frac{u_i}{v_i} viui 即可

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;

namespace MCMF
{
	const int MAXN=50+10,MAXM=200+10,inf=2e9;
	int dis[MAXN],visit[MAXN],pre[MAXN],maxflow,mincost;
	int maxvex,head[maxn],ecnt,s,t;
	vector<int> dd;
	
	void init(int ss,int tt,int n)
	{
	    ecnt=1,maxvex=n;
	    for(int i=0; i<=maxvex; ++i) head[i]=-1;
	    s=ss,t=tt;
	    dd.clear();
	}
	struct Edge
	{
	    int nxt,to,flow,cost;
	} edges[MAXM];
	void add(int u,int v,int flow,int cost)
	{
	    edges[++ecnt]= {head[u],v,flow,cost},head[u]=ecnt;
	    edges[++ecnt]= {head[v],u,0,-cost},head[v]=ecnt;
	}
	bool spfa()
	{
	    for(int i=0; i<=maxvex; ++i) dis[i]=inf,visit[i]=pre[i]=0;
	    dis[s]=0;
	    queue<int> q;
	    q.push(s);
	    visit[s]=1;
	    while(!q.empty())
	    {
	        int u=q.front();
	        q.pop();
	        visit[u]=0;
	        for(int i=head[u]; i!=-1; i=edges[i].nxt)
	        {
	            int v=edges[i].to,cost=edges[i].cost;
	            if(edges[i].flow&&dis[v]>dis[u]+cost)
	            {
	                dis[v]=dis[u]+cost;
	                pre[v]=i;
	                if(!visit[v]) visit[v]=1,q.push(v);
	            }
	        }
	    }
	    return dis[t]<inf;
	}
	void augment()
	{
		dd.push_back(dis[t]);
	    int p=t,curflow=inf;
	    while(pre[p])
	    {
	        curflow=min(curflow,edges[pre[p]].flow);
	        p=edges[pre[p]^1].to;
	    }
	    maxflow+=curflow,mincost+=curflow*dis[t];
	    p=t;
	    while(pre[p])
	    {
	        edges[pre[p]].flow-=curflow;
	        edges[pre[p]^1].flow+=curflow;
	        p=edges[pre[p]^1].to;
	    }
	}
	void mcmf()
	{
	    maxflow=0,mincost=0;
	    while(spfa()) augment();
	}
};
int n,m,s,t;
ll pref[maxn];
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		MCMF::init(1,n,n);
		for(int i=1;i<=m;++i)
		{
			int u,v,c;
			scanf("%d%d%d",&u,&v,&c);
			MCMF::add(u,v,1,c);
		}
		MCMF::mcmf();
		auto d=MCMF::dd;
		m=d.size();
		for(int i=1;i<=m;++i)
			pref[i]=pref[i-1]+d[i-1];
		int q,u,v;
		scanf("%d",&q);
		while(q--)
		{
			scanf("%d%d",&u,&v);
			if(1ll*m*u<v) puts("NaN");
			else
			{
				int x=v/u;
				int r=v%u;
				ll w=1ll*pref[x]*u+1ll*r*d[x];
				ll z=__gcd(w,1ll*v);
				printf("%lld/%lld\n",w/z,v/z);
			}
		}
	}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值