今天发现一个不错的东西,网络流部分的简明教程,用来当指南针好了:http://dev.10086.cn/cmdn/supesite/?uid-1461832-action-viewspace-itemid-6430
最小费用最大流
百度的词条今天竟然给力了ORZ,以下为抄袭:http://baike.baidu.com/view/638894.htm
解决途径和前面介绍的最大流算法思路相类似,一般首先给出零流作为初始流。这个流的费用为零,当然是最小费用的。然后寻找一条源点至汇点的增流链,但要求这条增流链必须是所有增流链中费用最小的一条。如果能找出增流链,则在增流链上增流,得出新流。将这个流做为初始流看待,继续寻找增流链增流。这样迭代下去,直至找不出增流链,这时的流即为最小费用最大流。这一算法思路的特点是保持解的最优性(每次得到的新流都是费用最小的流),而逐渐向可行解靠近(直至最大流时才是一个可行解)。
在这一算法中,为了寻求最小费用的增流链,对每一当前流,需建立伴随这一网络流的增流网络。例如图 1 网络G 是具有最小 费用的流,边旁参数为c(e) , f(e) , w(e),而图 2 即为该网络流 的增流网络G′。增流网络的顶点和原网络相同。 按以下原则建 立增流网络的边:若G中边(u,v)流量未饱,即f(u,v) < e(u,v),则G ' 中建边(u,v),赋权w ' (u,v)=w(u,v);若G中边(u, v)已有流量,即f(u,v)〉0,则G′中建边(v,u),赋权w′(v,u) =-w(u,v)。建立增流网络后,即可在此网络上求源点至汇点的最短路径,以此决定增流路径,然后在原网络上循此路径增流。这里,运用的仍然是最大流算法的增流原理,唯必须选定最小费用的增流链增流。
计算中有一个问题需要解决。这就是增流网络G ′中有负权边,因而不能直接应用标号法来寻找x至y的最短路径,采用其它计算有负权边的网络最短路径的方法来寻找x至y的最短路径,将 大大降低计算 效率。为了仍然采用标号法计算最短路径,在每次建立增流网络求得最短路径后,可将网络G的权w(e)做一次修正,使再建的增流网络不会出现负权边,并保证最短路径不至于因此而改变。下面介绍这种修改方法。 当流值为零,第一次建增流网络求最短路径时,因无负权边,当然可以采用标号法进行计算。为了使以后建立增流网络时不出现负权边,采取的办法是将 G中有流边(f(e)>0)的权w(e)修正为0。为此, 每次在增流网络上求得最短路径后,以下式计算G中新的边权w " (u,v): w " (u,v)=L(u)-L(v)+w(u,v) (*) 式中 L(u),L(v) -- 计算G′的x至y最短路径时u和v的标号值。第一次求最短径时如果(u,v)是增流路径上的边, 则据最短 路径算法一定有 L(v)=L(u)+w ' (u,v)=L(u)+w(u,v), 代入(*)式必有 w″(u,v)=0。 如果(u,v)不是增流路径上的边,则一定有: L(v)≤L(u)+w(u,v), 代入(*)式则有 w(u,v)≥0。 可见第一次修正w(e)后,对任一边,皆有w(e)≥0, 且有流 的边(增流链上的边),一定有w(e)=0。以后每次迭代计算,若 f(u,v)>0,增流网络需建立(v,u)边,边权数w ' (v,u)=-w(u,v) =0,即不会再出现负权边。 此外,每次迭代计算用(*)式修正一切w(e), 不难证明对每一条x至y的路径而言,其路径长度都同样增加L(x)-L(y)。因此,x至y的最短路径不会因对w(e)的修正而发生变化。 【计算步骤】 1. 对网络G=[V,E,C,W],给出流值为零的初始流。 2. 作伴随这个流的增流网络G′=[V′,E′,W′]。 G′的顶点同G:V′=V。 若G中f(u,v)<c(u,v),则G′中建边(u,v),w(u,v)=w(u,v)。 若G中f(u,v)>0,则G′中建边(v,u),w′(v,u)=-w(u,v)。 3. 若G′不存在x至y的路径,则G的流即为最小费用最大流, 停止计算;否则用标号法找出x至y的最短路径P。 4. 根据P,在G上增流: 对P的每条边(u,v),若G存在(u,v),则(u,v)增流;若G存在(v,u),则(v,u)减流。增(减)流后,应保证对任一边有c(e)≥ f(e)≥0。 5. 根据计算最短路径时的各顶点的标号值L(v),按下式修 改G一切边的权数w(e): L(u)-L(v)+w(e)→w(e)。 6. 将新流视为初始流,转2。
POJ 2135 Farm Tour
题意:FJ有N个农场,M条路,FJ要领朋友游玩,从1走到N,再回到1,不走重复路,每条路长度不一样,问最短路长为多少。
思路: 这个直接看题解用的最小费用最大流的思想解题,把每两个点之间的距离看成1,作为网络中容量的上界(和用最大流计算边联通度有类似)。按照最小费用流建模,若a到b之间有长度为w的路,ab之间费用为w,容量是1。ba之间费用为-w,容量是0。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
const int N=40000;
const int MAXE=20000000;
const int MAXV=1010;
const int inf=1<<30;
int head[N],s,t,cnt,V,E,ans;
int d[N],pre[N];
bool vis[N];
int q[MAXE];
struct Edge
{
int u,v,c,w,next;
}e[MAXE];
void addedge(int u,int v,int w,int c)
{
e[cnt].u=u;
e[cnt].v=v;
e[cnt].w=w;
e[cnt].c=c;
e[cnt].next=head[u];
head[u]=cnt++;
e[cnt].v=u;
e[cnt].u=v;
e[cnt].w=-w;
e[cnt].c=0;
e[cnt].next=head[v];
head[v]=cnt++;
}
bool SPFA(int s)
{
int l,r;
memset(pre,-1,sizeof(pre));
memset(vis,0,sizeof(vis));
for(int i=0;i<=t;i++) d[i]=inf;
d[s]=0;
l=0;r=0;
q[r++]=s;
vis[s]=1;
while(l!=r) //循环队列
{
int u=q[l++];
if(l>V) l=1;
vis[u]=0;
for(int j=head[u];j!=-1;j=e[j].next)
{
int v=e[j].v;
if(e[j].c>0&&d[u]+e[j].w<d[v])
{
d[v]=d[u]+e[j].w;
pre[v]=j;
if(!vis[v])
{
vis[v]=1;
if(r<=V && d[v]<d[q[l+1]]) q[--l]=v; //SLF优化
else {q[r++]=v;if(r>V) r=1;}
}
}
}
}
if(d[t]==inf)
return 0;
return 1;
}
void MCMF()
{
int flow=0;
while(SPFA(0))
{
ans+=d[t];
int u=t;
int mini=inf;
while(u!=s)
{
if(e[pre[u]].c<mini)
mini=e[pre[u]].c;
u=e[pre[u]].u;
}
flow+=mini;
u=t;
while(u!=s)
{
e[pre[u]].c-=mini;
e[pre[u]^1].c+=mini;
u=e[pre[u]].u;
}
}
}
int main()
{
freopen("in","r",stdin);
freopen("out","w",stdout);
while(scanf("%d%d",&V,&E)!=EOF)
{
s=0;t=++V;cnt=0;
for(int i=0;i<=t;i++)
head[i]=-1;
for(int i=1;i<=E;i++)
{
int u,v,cost;
scanf("%d%d%d",&u,&v,&cost);
addedge(u,v,cost,1);
addedge(v,u,cost,1);
}
addedge(0,1,0,2);
addedge(V-1,t,0,2);
ans=0;
MCMF();
printf("%d\n",ans);
}
return 0;
}
历史上的今天:我说世界变得太快?