关于网络流

昨天,确切的说应该是今天早上,搞到5点多,调试破了头还是没找出错误原因,早上醒来突发灵感,发现单位费用的边未反向,改后终于AC了,哈哈,开心啊,上阶段的练习算告一段落了。 

 

网络问题基本都是找增广路,再更新,直到某个条件不再满足即停止。

最大流(maxflow),每次找增广路P(augmented path),再找出增广路P中前向弧的最小剩余容量c(通俗点说就是瓶颈),更新各边,剩余网络(residual network)中反向弧要加上c,正向弧减去c,最大流量加上c,直到没有增广路即找到了最大流。

设M为网络中最大边容量,则Ford_Fulkerson算法任何的算法实现复杂度都是O(VM)。

增广路径的查找有好几种选择方式,在图算法书上看到随机测试结果,当容量为1时(如求二分图匹配,二分匹配问题可以归约到最大流问题)最短路径,最大容量,深度优先都差不多。但是当容量1-50随机时,最大容量效率高于最短路径,也远远高于深度优先(差着一个0啊)。

还有预流算法,最高标号预流推进,Relabel_to_Front算法,只做了一般预流,测试了几个题目,实际效率都低于Ford_Fulkerson,难道是我模板写的太差?问队友,都说不这么用到,算了,其它的以后有空再实现了。

 

最小费用最大流(mincost flow)有个环取消算法(cycle-cancleing algorithm)终止条件是当且仅当剩余网络不再包含负开销有向环,此时的最大流才是一个最小费用最大流。但这个方法得初始化哑边,每次环取消就是尽可能多的将流量移出哑边,但自己没实现过。

我的方法是每次在s-t之间找出费用最小的一条路径即单源最短路,如果t点不再被访问到,则算法终止。否则,按着最短路径找出最小剩余容量c,最大流量加上c,再更新最短路径上的边,前向弧减去c,反向弧加上c,并且造一条逆向的费用边,最小费用加上每条边的花销,每条边的花销=单位费用*c。

最小费用最大流既能求最小费用,又能得出最大流,是更为一般的模型。

 

附上自己第一次写的代码模板,请教路过的牛人提些建议。

/* 
网络中求最大流Ford_Fulkerson算法
参数含义:    m代表网络中节点数,第1节点为源点, 第m节点为汇点
            net[][]代表剩余网络
            path[]保存增广路径
            neck[]代表瓶颈,保存增广路径最小容量
返回值:        最大流量
*/

const int NMAX = 210;
int net[NMAX][NMAX];
int path[NMAX],n, m;

int bfs()
{
    queue
<int> SQ;
    
int neck[NMAX], i;
    memset(path,
-1,sizeof(path));
    path[
1= 0;
    neck[
1= INT_MAX;
    SQ.push(
1);

    
while(!SQ.empty()) {
        
int now = SQ.front();
        SQ.pop();
        
if(now == m) {
            
break ;
        }

        
for(i=2;i<=m;i++{
            
if(net[now][i] > 0 && path[i] == -1{
                path[i] 
= now;
                neck[i] 
= min(neck[now], net[now][i]);
                SQ.push(i);
            }

        }
    
    }

    
if(path[m] == -1{
        
return -1;
    }

    
return neck[m];
}


int Ford_Fulkerson()
{
    
int now, step;
    
int max_flow = 0;

    
while( (step=bfs()) != -1 ) {
        max_flow 
+= step;
        now 
= m;
        
while(now != 1{
            
int pre = path[now];
            net[pre][now] 
-= step;
            net[now][pre] 
+= step;
            now 
= pre;
        }

    }

    
return max_flow;
}

 

 

/**** **** **** **** **** ****
网络中最小费用最大流
参数含义:    n代表网络中的总节点数
            net[][]代表剩余网络
            cost[][]代表单位费用
            path[]保存增广路径
            ecost[]源点到各点的最短路
算法:初始最小费用和最大流均为,寻找单位费用最短路
在最短路中求出最大流,即为增广路,再修改剩余网络,直到无可增广路为止
返回值:        最小费用,最大流量
**** **** **** **** **** ***
*/
 
const int NMAX = 210;
int net[NMAX][NMAX], cost[NMAX][NMAX];
int path[NMAX], ecost[NMAX];
int n;
bool bellman_ford()
{
    
int i,j;
    memset(path,
-1,sizeof(path));
    fill(ecost, ecost
+NMAX, INT_MAX);
    ecost[
0= 0;

    
bool flag = true;
    
while(flag) {
        flag 
= false;
        
for(i=0;i<=n;i++{
            
if(ecost[i] == INT_MAX) {
                
continue ;
            }

            
for(j=0;j<=n;j++{
                
if(net[i][j] > 0 && ecost[i]+cost[i][j] < ecost[j]) {
                    flag 
= true;
                    ecost[j] 
= ecost[i]+cost[i][j];
                    path[j] 
= i;
                }

            }

        }

    }

    
return ecost[n] != INT_MAX;
}


int min_cost_max_flow()
{
    
int i,j;
    
int mincost = 0, maxflow = 0;
    
while( bellman_ford() ) {
        
int now = n;
        
int neck = INT_MAX;
        
while(now != 0{
            
int pre = path[now];
            neck 
= min(neck, net[pre][now]);
            now 
= pre;
        }

        maxflow 
+= neck;
        now 
= n;
        
while(now != 0{
            
int pre = path[now];
            net[pre][now] 
-= neck;
            net[now][pre] 
+= neck;
            cost[now][pre] 
= - cost[pre][now];
            mincost 
+= cost[pre][now] * neck;
            now 
= pre;
        }

    }

    
return mincost;
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值