zkw学习笔记
2016-01-03 09:57
351 查看
原文:http://www.artofproblemsolving.com/community/c1368h1020435
关于转化
首先是1.最小费用(可行)流
最小费用最大流
建立超级源
和超级汇
,
对顶点
, 若
添加边
, 若
添加边
, 之后求从
到
的最小费用最大流,
如果流量等于
, 就存在可行流,
残量网络已在原图上求出.
代码:
一开始脑子抽了没有理解为什么要用-ei后来黄学长跟我说ei<0 感觉自己蠢到家了。。。。。
2.最小费用最大流
最小费用(可行)流
连边
, 所有点
有
, 然后直接求.
3.最小费用(可行)流中负权边的消除
直接给负权边满流,然后转换成1
4.最小费用最大流中负权边的消除
先连边
, 使用
(3.) 中的方法消除负权边, 使用 (1.) 中的方法求出最小费用 (可行) 流, 之后距离标号不变, 再求最小费用最大流; 注意此时增广费用不能机械使用源点的标号——应该是源点汇点标号之差.
修改顶标
其实zkw与km有相似之处 都是修改顶标来找增广路的
详细看原文:
这里使用的是连续最短路算法.
最短路算法? 为什么程序里没有 SPFA? Dijkstra? 且慢, 先让我们回顾一下图论中最短路算法中的距离标号. 定义
为点
的距离标号,
任何一个最短路算法保证, 算法结束时对任意指向顶点
、从顶点
出发的边满足
(条件1),
且对于每个
存在一个
使得等号成立
(条件2). 换句话说, 任何一个满足以上两个条件的算法都可以叫做最短路, 而不仅仅是 SPFA、Dijkstra, 算法结束后, 恰在最短路上的边满足
.
在最小费用流的计算中, 我们每次沿
的路径增广后都不会破坏条件
1, 但是可能破坏了条件 2. 不满足条件 2 的后果是什么呢? 使我们找不到每条边都满足
新的增广路.
只好每次增广后使用 Dijkstra, SPFA 等等算法重新计算新的满足条件 2 的距离标号. 这无疑是一种浪费. KM 算法中我们可以修改不断修改可行顶标, 不断扩大可行子图, 这里也同样, 我们可以在始终满足条件 1 的距离标号上不断修改, 直到可以继续增广 (满足条件 2).
回顾一下 KM 算法修改顶标的方法. 根据最后一次寻找交错路不成功的 DFS, 找到
,
左边的点增加
,
右边的点减少
.
这里也一样, 根据最后一次寻找增广路不成功的 DFS, 找到 $ d = \min_{i \in V, j \notin V, u_{ij} > 0} \left\{ c_{ij} - D_i + D_j } \right\}$ ,
所有访问过的点距离标号增加
. 可以证明,
这样不会破坏性质 1, 而且至少有一条新的边进入了
的子图.
算法的步骤就是初始标号设为
,
不断增广, 如果不能增广, 修改标号继续增广, 直到彻底不能增广: 源点的标号已经被加到了
.
注意: 在程序中所有的 cost 均表示的是 reduced cost, 即
. 另外,
这个算法不能直接用于有任何负权边的图.
更不能用于负权圈的情况. 有关这两种情况的处理, 参见 (2.) 和 (3.) 中的说明.
代码:
自己的可以应对负权的
:
关于转化
首先是1.最小费用(可行)流
最小费用最大流
建立超级源
和超级汇
,
对顶点
, 若
添加边
, 若
添加边
, 之后求从
到
的最小费用最大流,
如果流量等于
, 就存在可行流,
残量网络已在原图上求出.
代码:
inline void New_Graph() { for(int i=3;i<=con;i++) if(e[i]) if(e[i]>0) addside(start,i,e[i],0); else addside(i,end,-e[i],0); else; }
一开始脑子抽了没有理解为什么要用-ei后来黄学长跟我说ei<0 感觉自己蠢到家了。。。。。
2.最小费用最大流
最小费用(可行)流
连边
, 所有点
有
, 然后直接求.
3.最小费用(可行)流中负权边的消除
直接给负权边满流,然后转换成1
inline void Begin() { int i,j,k,t; for(i=3;i<=con;i++) for(Chain *tp=Head[i];tp;tp=tp->next) if(tp->flow&&tp->cost<0) { tp->pair->flow+=tp->flow; e[i]-=tp->flow; e[tp->u]+=tp->flow; con_cost=tp->flow*tp->cost; tp->flow=0; } }
4.最小费用最大流中负权边的消除
先连边
, 使用
(3.) 中的方法消除负权边, 使用 (1.) 中的方法求出最小费用 (可行) 流, 之后距离标号不变, 再求最小费用最大流; 注意此时增广费用不能机械使用源点的标号——应该是源点汇点标号之差.
修改顶标
其实zkw与km有相似之处 都是修改顶标来找增广路的
详细看原文:
这里使用的是连续最短路算法.
最短路算法? 为什么程序里没有 SPFA? Dijkstra? 且慢, 先让我们回顾一下图论中最短路算法中的距离标号. 定义
为点
的距离标号,
任何一个最短路算法保证, 算法结束时对任意指向顶点
、从顶点
出发的边满足
(条件1),
且对于每个
存在一个
使得等号成立
(条件2). 换句话说, 任何一个满足以上两个条件的算法都可以叫做最短路, 而不仅仅是 SPFA、Dijkstra, 算法结束后, 恰在最短路上的边满足
.
在最小费用流的计算中, 我们每次沿
的路径增广后都不会破坏条件
1, 但是可能破坏了条件 2. 不满足条件 2 的后果是什么呢? 使我们找不到每条边都满足
新的增广路.
只好每次增广后使用 Dijkstra, SPFA 等等算法重新计算新的满足条件 2 的距离标号. 这无疑是一种浪费. KM 算法中我们可以修改不断修改可行顶标, 不断扩大可行子图, 这里也同样, 我们可以在始终满足条件 1 的距离标号上不断修改, 直到可以继续增广 (满足条件 2).
回顾一下 KM 算法修改顶标的方法. 根据最后一次寻找交错路不成功的 DFS, 找到
,
左边的点增加
,
右边的点减少
.
这里也一样, 根据最后一次寻找增广路不成功的 DFS, 找到 $ d = \min_{i \in V, j \notin V, u_{ij} > 0} \left\{ c_{ij} - D_i + D_j } \right\}$ ,
所有访问过的点距离标号增加
. 可以证明,
这样不会破坏性质 1, 而且至少有一条新的边进入了
的子图.
算法的步骤就是初始标号设为
,
不断增广, 如果不能增广, 修改标号继续增广, 直到彻底不能增广: 源点的标号已经被加到了
.
注意: 在程序中所有的 cost 均表示的是 reduced cost, 即
. 另外,
这个算法不能直接用于有任何负权边的图.
更不能用于负权圈的情况. 有关这两种情况的处理, 参见 (2.) 和 (3.) 中的说明.
代码:
#include <cstdio> #include <cstring> using namespace std; const int maxint=~0U>>1; int n,m,pi1,cost=0; bool v[550]; struct etype { int t,c,u; etype *next,*pair; etype(){} etype(int t_,int c_,int u_,etype* next_): t(t_),c(c_),u(u_),next(next_){} void* operator new(unsigned,void* p){return p;} } *e[550]; int aug(int no,int m) { if(no==n)return cost+=pi1*m,m; v[no]=true; int l=m; for(etype *i=e[no];i;i=i->next) if(i->u && !i->c && !v[i->t]) { int d=aug(i->t,l<i->u?l:i->u); i->u-=d,i->pair->u+=d,l-=d; if(!l)return m; } return m-l; } bool modlabel() { int d=maxint; for(int i=1;i<=n;++i)if(v[i]) for(etype *j=e[i];j;j=j->next) if(j->u && !v[j->t] && j->c<d)d=j->c; if(d==maxint)return false; for(int i=1;i<=n;++i)if(v[i]) for(etype *j=e[i];j;j=j->next) j->c-=d,j->pair->c+=d; pi1 += d; return true; } int main() { freopen("costflow.in","r",stdin); freopen("costflow.out","w",stdout); scanf("%d %d",&n,&m); etype *Pe=new etype[m+m]; while(m--) { int s,t,c,u; scanf("%d%d%d%d",&s,&t,&u,&c); e[s]=new(Pe++)etype(t, c,u,e[s]); e[t]=new(Pe++)etype(s,-c,0,e[t]); e[s]->pair=e[t]; e[t]->pair=e[s]; } do do memset(v,0,sizeof(v)); while(aug(1,maxint)); while(modlabel()); printf("%d\n",cost); return 0; }
自己的可以应对负权的
:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
char c;
inline void read(int &a)
{
a=0;do c=getchar();while(c<'0'||c>'9');
while(c<='9'&&c>='0')a=(a<<3)+(a<<1)+c-'0',c=getchar();
}
int con=2;
int Lastart=1,Laend=2;
int start,end;
struct Chain
{
int u,cost,flow;
Chain *next,*pair;
}*Head[100001];
int e[100001];
int con_cost;
int label[1000001];
int n,m;
inline void addside(int a,int b,int flow,int cost)
{
Chain *tp=new Chain;
tp->u=b;
tp->flow=flow;
tp->cost=cost;
tp->next=Head[a];
Head[a]=tp;
tp=new Chain;
tp->u=a;
tp->flow=0;
tp->cost=-cost;
tp->next=Head[b];
Head[b]=tp;
}
inline void Begin() { int i,j,k,t; for(i=3;i<=con;i++) for(Chain *tp=Head[i];tp;tp=tp->next) if(tp->flow&&tp->cost<0) { tp->pair->flow+=tp->flow; e[i]-=tp->flow; e[tp->u]+=tp->flow; con_cost=tp->flow*tp->cost; tp->flow=0; } }inline void New_Graph()
{
for(int i=3;i<=con;i++)
if(e[i])
if(e[i]>0) addside(Lastart,i,e[i],0);
else addside(i,Laend,-e[i],0);
else;
}
bool visited[1000001];
const
int INF=1<<29;
int DFS(int u,int F)
{
if(u==Laend)return con_cost+=F*(label[end]-label[start]),F;
int res=F;
visited[u]=true;
for(Chain *tp=Head[u];tp;tp=tp->next)
if(tp->flow&&!tp->cost&&!visited[tp->u])
{
int d=DFS(tp->u,min(tp->flow,res));
res-=d;
tp->flow-=d;
tp->pair->flow+=d;
if(!res)return F;
}
return F-res;
}
inline bool updata()
{
int a,d=INF,i;
for(i=1;i<=con;i++)
if(visited[i])
for(Chain *tp=Head[i];tp;tp=tp->next)
if(tp->flow&&!visited[tp->u]&&d>tp->cost)
d=tp->cost;
if(d==INF)return false;
for(i=1;i<=con;i++)
if(visited[i])
{
label[i]+=d;
for(Chain *tp=Head[i];tp;tp=tp->next)
tp->cost-=d,tp->pair->cost+=d;
}
return true;
}
int main()
{
read(n),read(m);
start=3,con=end=n+2;
addside(Lastart,start,INF,0);
addside(end,Laend,INF,0);
int a,b,v,d;
for(int i=1;i<=m;i++)
read(a),read(b),read(v),read(d),addside(a,b,v,d);
Begin();
New_Graph();
int maxflow=0;
do
do
memset(visited,0,sizeof(visited));
while(maxflow+=DFS(Lastart,INF));
while(updata());
printf("%d %d\n",maxflow,con_cost);
return 0;
}
相关文章推荐
- 151218设计模式之单例模式
- traincascade与AdaBoost的opencv实现框架
- VS2013崩溃,未找到与约束
- 第十五周 B-树的基本操作
- new 对象时类都干嘛去了?
- 151218final关键字的使用
- bestcoder round #1
- python基础知识
- 混合溶剂中的高分子刷
- .NET中MD5编码的内存泄露问题分析
- 2016年
- java中的继承
- Redis的Java客户端Jedis的八种调用方式(事务、管道、分布式…)介绍--转载
- 数字信号处理实验(一)——DTFT
- 取自己上一层目录路径。
- LeetCode总结 -- 树的性质篇
- UITableViewCell点击样式
- 0928-CALayer
- SDUT 2254 字母螺旋方阵(递归)
- 山河古人