dijkstra 最小费用最大流
前言:众所周知:spfa他死了
滑稽
dijkstra同样为最短路算法,为什么不能跑费用流qwq
好像是因为有负权边的缘故
但是如果我们如果使用某种玄学的将边权都拉回到正数的话
就可以跑了dijkstra,开心qwq
如果我们每条边暴力加上一个很大的值的话,我们还需要记录所经过的边数,还要保证不溢出,十分的毒瘤
尻考虑给每个节点一个势(ps:不是什么物理学算法
,就是为了给他起个名字)
然后将我们的最短路转移\(dis_v=dis_u+w\)改为\(dis_v=dis_u+w+h_u-h_v\)(\(h_i\)是势),保证\(w+h_u-h_v>=0\)
然后我们观察蔡依林(雾他对最短路有什么影响
比如说我们现在有一条\(p_1-p_2-p_3.....p_n\)的这么一条路径
其路径长度则为\((w_1+w_2+w_3...+w_{n-1})+(h_1-h_2)+(h_2-h_3)+(h_3-h_4)+......(h_{n-1}-h_n)\)
然后发现这个玩意\(\to ~ (h_1-h_2)+(h_2-h_3)+(h_3-h_4)+......(h_{n-1}-h_n)=h_1-h_n\)。如此这样,我们在算出加势以后的(无论路线是什么样的)最短路后,在减去\(h_{begin}-h_{end}\)就可以了
接下来的问题就变成了,如何确定一个\(h_i\).
我们先考虑变形一下\(w+h_u-h_v>=0~~\to~~h_u+w>=h_v\)
\(wow\),好像三角形不等式呀(在最短路中对于一条从\(u\)到\(v\)的有向边,总有\(dis_u+w>=dix_v\))。
是不是可以考虑将上一次的\(dis\)当做\(h_i\)(每次\(h_i+=dis_i\))呢?
是可以的,为什么?
假设现在有一条\(u\to v\)的有向边
- 假设他是一条权值是正的(不加势),那么肯定满足\(dis_u+w>=dis_v\)然就是最短路求错了。\(\therefore w+h_u-h_v>=0\)
- 如果是一条权值是负的话,我们的\(h_i\)是累加的\(dis_i\)的,所以必定存在某一次增广,是的\(v \to u\)的边变到了\(u \to v\)
然后这次增广(就是将\(v \to u\)的边反向的增广),肯定满足\(dis_v+w==dis_u(w>=0)~~~\to~~dis_v=dis_u-w\)
然后这时的\(dis_u,dis_v\)已经被我们累加到了\(h_u,h_v\)中,然后我们继续变形\(dis_u-w-dis_v==0 ~~~\to~~ h_u-h_v-w>=0\)
然后\(-w\)是\(u\to v\)这条边的权值。所以这次并不会成为负数
\(\mathcal{So}\)
我们这样的话就能跑dijkstra了。开心\(qwq\)
而且更快,更稳定,也不容易猝死
↓及其丑陋的代码
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
const int maxn=101000;
using std::swap;
using std::min;
struct Edge
{
int p;
int w;
int f;
int nxt;
};
struct Data
{
int p;
int d;
bool operator <(const Data &a)const
{
return d<a.d;
}
};
int n,m,s,t;
int len;
Data base[maxn<<6];
Data top()
{
return base[1];
}
void pop()
{
swap(base[1],base[len--]);
int pos=1;
int nxt;
while(true)
{
nxt=pos;
if(base[pos<<1]<base[nxt]&&(pos<<1)<=len)
nxt=pos<<1;
if(base[pos<<1|1]<base[nxt]&&(pos<<1|1)<=len)
nxt=pos<<1|1;
if(pos==nxt) break;
swap(base[pos],base[nxt]);
pos=nxt;
}
}
void push(Data val)
{
base[++len]=val;
int pos=len;
while((pos>>1)&&base[pos]<base[pos>>1])
{
swap(base[pos>>1],base[pos]);
pos>>=1;
}
return ;
}
Edge line[maxn<<1];
int head[maxn],tail=-1;
void add(int a,int b,int c,int d)
{
line[++tail].p=b;
line[tail].w=c;
line[tail].f=d;
line[tail].nxt=head[a];
head[a]=tail;
}
int h[maxn];
bool vis[maxn];
int dis[maxn];
int from[maxn];
int L[maxn];
int flow[maxn];
int Max_flow,Min_cost;
bool dijkstra(int begin,int end)
{
len=0;
for(int i=1;i<=n;i++)
{
dis[i]=0x7fffffff;
flow[i]=0x7fffffff;
from[i]=L[i]=vis[i]=0;
}
dis[begin]=0;
Data pas;
pas.p=begin;pas.d=0;
push(pas);
while(len)//手写堆怪我喽
{
pas=top();pop();
while(vis[pas.p]&&len>=1)
{
pas=top();
pop();
}
if(vis[pas.p]&&!len) break;
vis[pas.p]=true;
dis[pas.p]=pas.d;
for(int i=head[pas.p];i!=-1;i=line[i].nxt)
if(line[i].f>0&&!vis[line[i].p]&&dis[line[i].p]>dis[pas.p]+line[i].w+h[pas.p]-h[line[i].p])//判断,带上势
{
dis[line[i].p]=dis[pas.p]+line[i].w+h[pas.p]-h[line[i].p];//跟spfa一样的套路,就是多了个势
flow[line[i].p]=min(line[i].f,flow[pas.p]);
from[line[i].p]=pas.p;
L[line[i].p]=i;
Data nxt;
nxt.p=line[i].p;nxt.d=dis[line[i].p];
push(nxt);
}
}
return dis[end]!=0x7fffffff;
}
void MCMA(int begin,int end)
{
while(dijkstra(begin,end))//差不多跟spfa一样的格式,就是加了个h数组
{
int max_flow=flow[end];
Min_cost+=max_flow*(dis[end]-h[begin]+h[end]);
Max_flow+=max_flow;
for(int i=end;i!=begin;i=from[i])
{
line[L[i]].f-=max_flow;
line[L[i]^1].f+=max_flow;
}
for(int i=1;i<=n;i++)
h[i]+=dis[i];//累加,一定要累加,虽然不累加可能过几个点
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=n;i++) head[i]=-1;
for(int i=1;i<=m;i++)
{
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
add(a,b,d,c);add(b,a,-d,0);//建边
}
MCMA(s,t);//跑费用流
printf("%d %d",Max_flow,Min_cost);//输出
return 0;
}
dijkstra 最小费用最大流的更多相关文章
- UVa 10806 Dijkstra,Dijkstra(最小费用最大流)
裸的费用流.往返就相当于从起点走两条路到终点. 按题意建图,将距离设为费用,流量设为1.然后增加2个点,一个连向节点1,流量=2,费用=0;结点n连一条同样的弧,然后求解最小费用最大流.当且仅当最大流 ...
- [板子]最小费用最大流(Dijkstra增广)
最小费用最大流板子,没有压行.利用重标号让边权非负,用Dijkstra进行增广,在理论和实际上都比SPFA增广快得多.教程略去.转载请随意. #include <cstdio> #incl ...
- P3381 【模板】最小费用最大流
P3381 [模板]最小费用最大流 题目描述 如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 输入输出格式 输入格式: 第一行 ...
- Luogu--3381 【模板】最小费用最大流
题目链接 3381 [模板]最小费用最大流 手写堆版本 dijkstra 400+ms 看来优先队列的常数好大 #include<bits/stdc++.h> using namesp ...
- POJ 2135 Farm Tour (网络流,最小费用最大流)
POJ 2135 Farm Tour (网络流,最小费用最大流) Description When FJ's friends visit him on the farm, he likes to sh ...
- 经典贪心算法(哈夫曼算法,Dijstra单源最短路径算法,最小费用最大流)
哈夫曼编码与哈夫曼算法 哈弗曼编码的目的是,如何用更短的bit来编码数据. 通过变长编码压缩编码长度.我们知道普通的编码都是定长的,比如常用的ASCII编码,每个字符都是8个bit.但在很多情况下,数 ...
- [Ahoi2014]支线剧情[无源汇有下界最小费用可行流]
3876: [Ahoi2014]支线剧情 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1538 Solved: 940[Submit][Statu ...
- 2017"百度之星"程序设计大赛 - 初赛(B) 度度熊的交易计划 最小费用最大流求最大费用
/** 题目:度度熊的交易计划 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6118 题意:度度熊参与了喵哈哈村的商业大会,但是这次商业大会遇到了一个难题 ...
- 经典网络流题目模板(P3376 + P2756 + P3381 : 最大流 + 二分图匹配 + 最小费用最大流)
题目来源 P3376 [模板]网络最大流 P2756 飞行员配对方案问题 P3381 [模板]最小费用最大流 最大流 最大流问题是网络流的经典类型之一,用处广泛,个人认为网络流问题最具特点的操作就是建 ...
随机推荐
- java NIO之SelectedKey
SelectedKey是channel与Selector绑定的标记,每将一个channel注册到一个selector就会产生一个SelectedKey,并将这个SelectedKey放入到Select ...
- 九度oj题目1153:括号匹配问题
题目1153:括号匹配问题 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4866 解决:2112 题目描述: 在某个字符串(长度不超过100)中有左括号.右括号和大小写字母:规定(与常见 ...
- "Sorry this application cannot run under a virtual machine" Error
错误: 运行一个程序是出现了 “sorry this application cannot run under a virtual machine” 错误. 如何解决: 控制面板-->卸载程序- ...
- Class.forName和ClassLoader的区别
一 看名字就知道了,一个是类的创建,一个类加载器 二 再看下Class.forName源码,调用了ClassLoader @CallerSensitive public static Class< ...
- .net 金额中文大写 日期转中文
金额中文大写 #region 中文大写 /// <summary> /// 返回中文数字 ,如壹佰元整 /// </summary> /// <param name=&q ...
- linux防火墙与端口设置
1.编辑iptables文件 # sudo vi /etc/sysconfig/iptables 添加如下一行 -A INPUT -p tcp -m state --state NEW -m tcp ...
- node.js获取cookie
node.js 获取cookie var Cookies ={}; if (req.headers.cookie != null) { req.headers.cookie.split(';').fo ...
- 日期控件html
日期控件多的是,这里收录的是最简单的一种 <html> <head> <script type="text/javascript"> funct ...
- hdu 1087 最大递增和
思路和LIS差不多,dp[i]为i结尾最大值 #include <iostream> #include <string> #include <cstring> #i ...
- 集合之equals与hashCode方法
一 equals equals方法是Object级的,默认对比两个对象的内存地址,很多类都重写了该方法,对比对象的实际内容,一般对比同一类对象相同属性的属性值是否相同. 二 hashCode 1.哈 ...
,就是为了给他起个名字)