求网络的最小费用最大流【好资料】

本文介绍了两种求解网络中最小费用最大流的经典算法——Ford和Fulkerson迭加算法及圈算法。通过实例演示了算法的具体步骤,并提供了C语言实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 转自:http://charpn.spaces.live.com/blog/cns!AC6DB5E1F5B033F6!204.entry

求网络的最小费用最大流

说明:网上找的资料,没有图,根据给的信息自己做了一些图,只看了第一个算法,并加入了自己的修改。

求网络的最小费用最大流

一.Ford和Fulkerson迭加算法.
基本思路:把各条弧上单位流量的费用看成某种长度,用求解最短路问题的方法确定一条自V1至Vn的最短路;再将这条最短路作为可扩充路,用求解最大流问题的方法将其上的流量增至最大可能值;而这条最短路上的流量增加后,其上各条弧的单位流量的费用要重新确定,如此多次迭代,最终得到最小费用最大流.
迭加算法:
1) 给定目标流量F或∞,给定最小费用的初始可行流{fij}=0
2) 若V(f)=F,停止,f为最小费用流;否则转(3).
3) 构造{ fij } 相应的新的费用有向图W(fij),在W(fij)寻找Vs到Vt的最小费用有向路P(最短路),沿P增加流f的流量直到F,转(2);若不存在从Vs到Vt的最小费用的有向路P,停止.f就是最小费用最大流.
具体解题步骤:
设图中双线所示路径为最短路径,费用有向图为W(fij).

clip_image002

图(a) 注:弧旁的数字是容量(运费)

clip_image004 clip_image006

图(b) 图(c)

clip_image008 clip_image010

图(d) 图(e) 容量,实际流量(费用)

在图(a)中给出零流 f,找到最小费用有向路(1-->2-->4-->6),修改图(a)中的可行流,δ=min{4,3,5}=3, 并调整容量,得图(b),以图(b)为基础,再构造相应的新的最小费用有向路(1-->2-->3-->5-->4-->6), 修改图(b)中的可行流, δ=min{1,1,2,1,2}=1, 并调整容量,得图(c), 再以图(c)为基础,构造出新的最小费用有向路(1-->3-->5-->6), 修改图(c)中的可行流,δ=min{5,1,2}=1, 再调整容量,此时得图(d)中已无最小费用有向路,停止,经计算:
图(e)中 最小费用=1*4+3*3+2*4+4*1+1*1+4*2+1*1+3*1=38
图(e)中 最大流=5

C语言程序如下(运行通过):
/*Ford和Fulkerson迭加算法*/

#include"stdio.h"

void main()

{

int a,b,i,j,k,p,n,B[10][10],C[10][10],D[10][10],P[10][10][10];

printf("please input n:/n");

scanf("%d",&n);

printf("please input C[%d][%d],B[%d][%d]:/n",n,n,n,n);

for(i=0;i<=n;i++)

for(j=0;j<=n;j++)

scanf("%7d,%7d",&C[i][j],&B[i][j]); //输入各点(i,j)之间的容量C[i][j]和运费B[i][j]

for(i=0;i<=n;i++)

for(j=0;j<=n;j++)

{

D[i][j]=B[i][j];

for(k=0;k<=n;k++)

P[i][j][k]=0;

if(D[i][j]<100) //注:100表示Vi到Vj之间无可行路

{

P[i][j][i]=1;P[i][j][j]=1;//???

}

}

for(k=0;k<=n;k++)

for(i=0;i<=n;i++)

for(j=0;j<=n;j++)

if(D[i][k]+D[k][j]<D[i][j])

{

D[i][j]=D[i][k]+D[k][j];

for(p=0;p<=n;p++)

P[i][j][p]=P[i][k][p]||P[k][j][p];//???

} //调用FLOYD算法

printf("D[%d][%d]:/n",n,n);

for(i=0;i<=n;i++)

{

for(j=0;j<=n;j++)

printf("%7d",D[i][j]);

printf("/n");

} //由表D中的数值确定V0到V5的最短路

a=C[1][3];

b=B[1][3]*D[0][5];

printf("a=%d,b=%d/n",a,b);

B[1][3]=100; //将容量已满的路改为不可行路

for(i=0;i<=n;i++)

for(j=0;j<=n;j++)

{

D[i][j]=B[i][j];

for(k=0;k<=n;k++)

P[i][j][k]=0;

if(D[i][j]<100)

{

P[i][j][i]=1;P[i][j][j]=1;

}

}

for(k=0;k<=n;k++)

for(i=0;i<=n;i++)

for(j=0;j<=n;j++)

if(D[i][k]+D[k][j]<D[i][j])

{

D[i][j]=D[i][k]+D[k][j];

for(p=0;p<=n;p++)

P[i][j][p]=P[i][k][p]||P[k][j][p];

} //调用FLOYD算法

printf("D[%d][%d]:/n",n,n);

for(i=0;i<=n;i++)

{

for(j=0;j<=n;j++)

printf("%7d",D[i][j]);

printf("/n");

} //由表D中的数值确定V0到V5的新最短路

a=a+C[1][2];b=b+D[0][5];

printf("a=%d,b=%d/n",a,b);

B[0][1]=100;B[1][2]=100;B[4][3]=100; //将容量已满的路改为不可行路

for(i=0;i<=n;i++)

for(j=0;j<=n;j++)

{

D[i][j]=B[i][j];

for(k=0;k<=n;k++)

P[i][j][k]=0;

if(D[i][j]<100)

{

P[i][j][i]=1;P[i][j][j]=1;

}

}

for(k=0;k<=n;k++)

for(i=0;i<=n;i++)

for(j=0;j<=n;j++)

if(D[i][k]+D[k][j]<D[i][j])

{

D[i][j]=D[i][k]+D[k][j];

for(p=0;p<=n;p++)

P[i][j][p]=P[i][k][p]||P[k][j][p];

} //调用FLOYD算法

printf("D[%d][%d]:/n",n,n);

for(i=0;i<=n;i++)

{

for(j=0;j<=n;j++)

printf("%7d",D[i][j]);

printf("/n");

} //由表D中的数值确定V0到V5的新最短路

a=a+C[2][4]-C[4][3];b=b+D[0][5];

printf("a=%d,b=%d/n",a,b);

B[2][4]=100; //将容量已满的路改为不可行路

for(i=0;i<=n;i++)

for(j=0;j<=n;j++)

{

D[i][j]=B[i][j];

for(k=0;k<=n;k++)

P[i][j][k]=0;

if(D[i][j]<100)

{

P[i][j][i]=1;P[i][j][j]=1;

}

}

for(k=0;k<=n;k++)

for(i=0;i<=n;i++)

for(j=0;j<=n;j++)

if(D[i][k]+D[k][j]<D[i][j])

{

D[i][j]=D[i][k]+D[k][j];

for(p=0;p<=n;p++)

P[i][j][p]=P[i][k][p]||P[k][j][p];

} //调用FLOYD算法

printf("D[%d][%d]:/n",n,n);

for(i=0;i<=n;i++)

{

for(j=0;j<=n;j++)

printf("%7d",D[i][j]);

printf("/n");

} //检验有无V0到V5的新最短路

}

运行结果:
please input n:
5
please input C[5][5],B[5][5]:
0,0 4,1 5,3 0,100 0,100 0,100
0,100 0,0 1,1 3,3 0,100 0,100
0,100 0,100 0,0 0,100 2,4 0,100
0,100 0,100 0,100 0,0 0,100 5,2
0,100 0,100 0,100 1,1 0,0 2,4
0,100 0,100 0,100 0,100 0,100 0,0
D[5][5]:
0 1 2 4 6 6
100 0 1 3 5 5
100 100 0 5 4 7
100 100 100 0 100 2
100 100 100 1 0 3
100 100 100 100 100 0
a=3,b=18
D[5][5]:
0 1 2 7 6 9
100 0 1 6 5 8
100 100 0 5 4 7
100 100 100 0 100 2
100 100 100 1 0 3
100 100 100 100 100 0
a=4,b=27
D[5][5]:
0 100 3 100 7 11
100 0 100 100 100 100
100 100 0 100 4 8
100 100 100 0 100 2
100 100 100 100 0 4
100 100 100 100 100 0
a=5,b=38
D[5][5]:
0 100 3 100 100 100
100 0 100 100 100 100
100 100 0 100 100 100
100 100 100 0 100 2
100 100 100 100 0 4
100 100 100 100 100 0
//注:100表示Vi到Vj之间无可行路

二.圈算法:
1) 利用Ford和Fulkson标号算法找出流量为F(<=最大流)的流f.
2) 构造f对应的调整容量的流网络N'(f).
3) 搜索N'(f)中的负费用有向图C(Floyd算法),若没有则停止,否则转(4).
4) 在C上找出最大的循环流,并加到N上去,同时修改N'(F)中C的容量,转(3).
具体解题步骤:

利用Ford和Fulkson标号算法,得网络的最大流F=5,见图(a),由图(a)构造相应的调整容量的流网络(b),图(b)中不存在负费用有向图,故停止.经计算:
图(b)中 最小费用= 4*1+3*1+1*1+3*3+4*2+1*1+4*1+2*4=38
图(a)中 最大流为F=5

C语言程序如下(运行通过):
/*圈算法*/
#include"stdio.h"
int min(int x,int y)
{if(x<y) return(x);
else return(y);
}
void main()
{int a=0,b=0,i,j,k,p,n,t,A[10][10],P[10][10],B[10][10],C[10][10],D[10][10];
printf("please input n:/n");
scanf("%d",&n);
printf("please input C[%d][%d],B[%d][%d]:/n",n,n,n,n);
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
scanf("%7d,%7d",&C[i][j],&B[i][j]); //输入各点(i,j)之间的容量C[i][j]和运费B[i][j]
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
{A[i][j]=C[i][j];D[i][j]=0;P[i][j]=B[i][j];}
for(i=n;i>1;i--)
for(j=i-1;j>0;j--)
for(k=j-1;k>=0;k--)
if(A[j][i]!=0&&A[k][j]!=0)
D[k][j]=min(A[j][i],A[k][j]);
printf("D[%d][%d]:/n",n,n);
for(i=0;i<=n;i++)
{for(j=0;j<=n;j++)
printf("%7d",D[i][j]);
printf("/n");
}
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
for(k=j+1;k<=n;k++)
if(D[i][j]!=0&&D[j][k]!=0)
if(D[i][j]+D[j][k]==C[i][j])
{A[i][j]=0;A[j][k]=0;A[j-i][k-j]=0;
P[i][j]=100;P[j][k]=100;P[j-i][k-j]=0;
a=a+C[i][j];
b=b+B[i][j]*C[i][j]+B[j][k]*C[j][k]+B[j-i][k-j]*C[j-i][k-j];
for(p=k+1;p<=n;p++)
if(C[i][j]<C[k][p])
{b=b+B[k][p]*C[i][j];
A[k][p]=C[k][p]-C[i][j];
}
for(p=k-j+1;p<=n;p++)
if(C[j-i][k-j]<C[k-j][p])
{b=b+B[k-j][p]*C[j-i][k-j]+B[4][3]*C[4][3];
A[k-j][p]=C[k-j][p]-C[j-i][k-j];
}
A[4][3]=0;
}
printf("a=%d,b=%d/n",a,b);
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
D[i][j]=0;
for(i=n;i>1;i--)
for(j=i-1;j>0;j--)
for(k=j-1;k>=0;k--)
if(A[j][i]!=0&&A[k][j]!=0)
D[k][j]=min(A[j][i],A[k][j]);
printf("D[%d][%d]:/n",n,n);
for(i=0;i<=n;i++)
{for(j=0;j<=n;j++)
printf("%7d",D[i][j]);
printf("/n");
}
for(i=0;i<n-1;i++)
for(j=i+1;j<n;j++)
for(k=j+1;k<=n;k++)
if(D[i][j]!=0&&D[j][k]!=0)
if(D[i][j]==D[j][k])
for(p=k+1;p<=n;p++)
{t=min(min(A[i][j],A[j][k]),min(A[j][k],A[k][p]));
a=a+t;
b=b+t*(B[i][j]+B[j][k]+B[k][p]);
}
printf("a=%d,b=%d/n",a,b);
}

运行结果:
please input n:
5
please input C[5][5],B[5][5]:
0,0 4,1 5,3 0,100 0,100 0,100
0,100 0,0 1,1 3,3 0,100 0,100
0,100 0,100 0,0 0,100 2,4 0,100
0,100 0,100 0,100 0,0 0,100 5,2
0,100 0,100 0,100 1,1 0,0 2,4
0,100 0,100 0,100 0,100 0,100 0,0
D[5][5]:
0 1 2 0 0 0
0 0 1 3 0 0
0 0 0 0 2 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
a=4,b=27
D[5][5]:
0 0 1 0 0 0
0 0 0 0 0 0
0 0 0 0 1 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
a=5,b=38
//注:100表示Vi到Vj之间无可行路

 

 

 
没有名字 发表:
请问图D中
顶点4到顶点6的那条边
是不是写错了
应该是1(2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

helihui123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值