• [模板] 最小费用最大流


    一、题目

    点此看题

    二、解法

    讲一种势能 ( t dijkstra) 的做法(简称势能算法),因为 ( t spfa) 在单次扩展的时候可能会被卡到 (O(nm)),而势能 ( t dijkstra) 的时间复杂度是严格的 (O(mlog n)),在一些扩展次数较小的毒瘤题中可能会用到。

    势能算法的中心思路是化负权边为非负权边,我们设计势能函数 (h(x)) 来调整边权,这里的势能和物理中的定义是很相似的,也就是势能变化之和初末位置有关,而和路径无关。

    (w'(u,v)=w(u,v)+h(u)-h(v)),不难发现最后跑出来的最短路要加上 (h(t)-h(s))

    关键是如何使得 (w(u,v)+h(u)-h(v)geq0) 恒成立,设 (d(u))(s)(u) 真正的最短路,设 (dis(u))(s)(u) 通过转化后的边求出来的最短路,令 (h(s)=0),有 (dis(u)+h(u)=d(u))

    如果我们再每次做完最短路之后让 (h(u)leftarrow d(u)) 是满足条件的,因为如果是原来的边那么显然 (d(u)-d(v)+w(u,v)geq 0),如果是新产生的取负的边那么 (d(u)=d(v)+w,(w>0) ightarrow d(u)-d(v)-w=0),新的边权就是 (0),也满足条件。那么每次我们把 (h(u)) 加上 (dis(u)) 就可以维护势能函数了。

    时间复杂度 (O(flowcdot mlog n))

    #include <cstdio>
    #include <queue>
    using namespace std;
    const int M = 50005;
    const int inf = 0x3f3f3f3f;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,s,t,tot,f[M],dis[M],fw[M],pre[M],lst[M],h[M];
    struct edge
    {
    	int v,f,c,next;
    }e[2*M];
    struct node
    {
    	int u,c;
    	bool operator < (const node &b) const
    	{
    		return c>b.c;
    	}
    };
    void add(int u,int v,int F,int c)
    {
    	e[++tot]=edge{v,F,c,f[u]},f[u]=tot;
    	e[++tot]=edge{u,0,-c,f[v]},f[v]=tot;
    }
    int bfs()
    {
    	for(int i=1;i<=n;i++) dis[i]=inf;
    	fw[s]=inf;pre[s]=lst[s]=dis[s]=0;
    	priority_queue<node> q;
    	q.push(node{s,0});
    	while(!q.empty())
    	{
    		node t=q.top();q.pop();int u=t.u;
    		for(int i=f[u];i;i=e[i].next)
    		{
    			int v=e[i].v,c=h[u]-h[v]+e[i].c;
    			if(dis[v]>dis[u]+c && e[i].f)
    			{
    				dis[v]=dis[u]+c;
    				pre[v]=u;lst[v]=i;
    				fw[v]=min(fw[u],e[i].f);
    				q.push(node{v,dis[v]});
    			}
    		}
    	}
    	return dis[t]<inf;
    }
    signed main()
    {
    	n=read();m=read();s=read();t=read();tot=1;
    	for(int i=1;i<=m;i++)
    	{
    		int u=read(),v=read(),f=read(),c=read();
    		add(u,v,f,c);
    	}
    	int ans=0,flow=0;
    	while(bfs())
    	{
    		int zy=t;
    		flow+=fw[t];
    		ans+=(dis[t]+h[t])*fw[t];
    		while(zy!=s)
    		{
    			e[lst[zy]].f-=fw[t];
    			e[lst[zy]^1].f+=fw[t];
    			zy=pre[zy];
    		}
    		for(int i=1;i<=n;i++)
    			h[i]+=dis[i];
    	}
    	printf("%d %d
    ",flow,ans);
    }
    
  • 相关阅读:
    【分享】HTML5附件拖拽上传drop & google.gears
    【分享】return false,对阻止事件默认动作的一些测试
    【记录】随笔分类汇总
    【分享】微博 @ 符号的用户名提示效果。(想@到谁?)
    【记录】File, FileReader 和 Ajax 文件上传
    【动态】简单的JS动态加载单体
    【分享】简单页面提示插件第二版表单验证很简单
    【记录】GIT 常用命令
    【分享】jQuery animate自定义动画的简单实现
    【分享】 封装js操作textarea 方法集合(兼容很好)。
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15145143.html
Copyright © 2020-2023  润新知